Hardware components | ||||||
![]() |
| × | 1 | |||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
Hand tools and fabrication machines | ||||||
![]() |
| |||||
![]() |
| |||||
![]() |
|
Growing up, I always loved going to arcades, and more specifically, I loved the arcade basketball games. The joy of the ding for every made basket and the thrill of time expiring as I shoot my final shots is something that will always resonate somewhere deep inside of me. With my project, I designed a miniature version of this that could be set up on a desk and played at home. Now, when I want a quick break from work, I have a fully functioning miniature basketball game to bring back the childhood sensation of hitting a last second shot to set a new high score!
OverviewWhen the device is started up, the LCD will prompt the user to press the button when they are ready to begin the game. Once they press the button, a countdown will begin from 5, which once complete will prompt the LCD to display "Begin!", and the game will begin with a 20 second timer. The user can then shoot any of the balls, with their hand behind the edge of the box, and the IR break beams will be monitoring if they made a shot. Every made basket will play a noise from the piezo element and add two points to the scoreboard. After 20 seconds, the game is over, and the user can look at their score.
Building Process
To begin, I 3-D printed my hoop, rim, and base separately. The CAD files to create the drawing is shown later in this page. I then glued them together, to work as one functioning hoop.
I then had to wire each piece separately, for which I did at first on the bread board itself to further ensure any issues with wiring were likely due to human input rather than faulty wiring. I first wired the potentiometer, which is used to vary the backlight on the LCD display.
Next, I wired the LCD display, to make sure the potentiometer was working.
After continuing to wire each part individually, it then became time to build my housing and implement my components into it. I decided to craft a box into rectangular arena, with a tall back that would have all of my components wired from behind. This allowed me to put my breadboard with all wired parts into on the back bottom, out of field of view for the player. I then had to take my components off of the breadboard, and use various methods to have them facing the user on the front of the box back. Although I mainly used Female wiring components, there were only so many, and for the start button I had to solder male wires to it.
The rest of the components wired to the breadboard and implemented into my device take up most of the back of the housing.
For the balls to play with, they began as orange pom poms which I used sharpie to make look like basketballs.
Unfortunately, these were too small for the IR breakbeam to consistently detect due to the rim size. To combat this, I made the balls slightly bigger by wrapping them in tin foil.
Final Product
BangNote
Python"""
--------------------------------------------------------------------------
BangNote
--------------------------------------------------------------------------
License:
Copyright 2023 Mason Melendez
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------
Use IR breakbeam to detect a made basket. Then, play a note with a piezo element. Finally, add two points to the scoreboard.
Requirements:
- Play a note when the ir beam is broken.
- Display the fill percentage using measurements from the force sensitive
resistor
"""
import sys
import time
import math
import random
import Adafruit_BBIO.ADC as ADC
import Adafruit_BBIO.GPIO as GPIO
import Adafruit_BBIO.PWM as PWM
from ht16k33_project import HT16K33
# ------------------------------------------------------------------------
# Global variables
# ------------------------------------------------------------------------
servo_pin = "P1_36"
piezo_pin = "P2_3"
FSR_pin = "AIN5"
ir_pin = "P2_2"
# ------------------------------------------------------------------------
# Note Library
# ------------------------------------------------------------------------
NOTE_F5 = 698
NOTE_C7 = 2093
# ------------------------------------------------------------------------
# Main Tasks
# ------------------------------------------------------------------------
def setup():
"""Sets up the hardware components."""
ADC.setup()
GPIO.setup(ir_pin, GPIO.IN)
""" HT16K33.display_setup()
HT16K33.display_clear() """
# end def
def play_note(Note, Length):
"""Plays a given note for a given length."""
PWM.start(piezo_pin, 50, Note)
time.sleep(Length)
PWM.stop(piezo_pin)
PWM.cleanup() #Stops continuous tone after function runs
# end def
def bang():
"""Plays a sound for a made basket"""
play_note(NOTE_F5, 0.1) # Play the first note
time.sleep(0.1) # Add a short pause between the notes
play_note(NOTE_C7, 0.2) # Play the final note with a longer duration
# end def
# ------------------------------------------------------------------------
# Main script
# ------------------------------------------------------------------------
if __name__ == '__main__':
setup()
display = HT16K33(1, 0x70)
bang()
score = 0
while True:
if GPIO.input("P2_2") == 0:
#Basket is made
print("basket")
score += 2
#Update display
display.update(score)
bang()
time.sleep(0.05)
else:
pass
time.sleep(0.1)
ht16k33_project.py
Python"""
--------------------------------------------------------------------------
HT16K33 I2C Library
--------------------------------------------------------------------------
License:
Copyright 2023 Mason Melendez
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------
"""
import os
import time
# ------------------------------------------------------------------------
# Functions / Classes
# ------------------------------------------------------------------------
# Constants
HEX_DIGITS = [0x3f, 0x06, 0x5b, 0x4f, # 0, 1, 2, 3
0x66, 0x6d, 0x7d, 0x07, # 4, 5, 6, 7
0x7f, 0x6f, 0x77, 0x7c, # 8, 9, A, b
0x39, 0x5e, 0x79, 0x71] # C, d, E, F
LETTERS = { "a" : 0x77, "A" : 0x77, # "A"
"b" : 0x7c, "B" : 0x7c, # "b"
"c" : 0x58, "C" : 0x39, # "c", "C"
"d" : 0x5e, "D" : 0x5e, # "d"
"e" : 0x79, "E" : 0x79, # "E"
"f" : 0x71, "F" : 0x71, # "F"
"g" : 0x6F, "G" : 0x6F, # "g"
"h" : 0x74, "H" : 0x76, # "h", "H"
"i" : 0x04, "I" : 0x30, # "i", "I"
"j" : 0x0e, "J" : 0x0e, # "J"
# Cannot be implemented "k" : None, "K" : None,
"l" : 0x38, "L" : 0x38, # "L"
# Cannot be implemented "m" : None, "M" : None,
"n" : 0x54, "N" : 0x54, # "n"
"o" : 0x5c, "O" : 0x3f, # "o", "O"
"p" : 0x73, "P" : 0x73, # "P"
"q" : 0x67, "Q" : 0x67, # "q"
"r" : 0x50, "R" : 0x50, # "r"
"s" : 0x6D, "S" : 0x6D, # "S"
"t" : 0x78, "T" : 0x78, # "t"
"u" : 0x1c, "U" : 0x3e, # "u", "U"
# Cannot be implemented "v" : None, "V" : None,
# Cannot be implemented "w" : None, "W" : None,
# Cannot be implemented "x" : None, "X" : None,
"y" : 0x6e, "Y" : 0x6e, # "y"
# Cannot be implemented "z" : None, "Z" : None,
" " : 0x00, # " "
"-" : 0x40, # "-"
"0" : 0x3f, # "0"
"1" : 0x06, # "1"
"2" : 0x5b, # "2"
"3" : 0x4f, # "3"
"4" : 0x66, # "4"
"5" : 0x6d, # "5"
"6" : 0x7d, # "6"
"7" : 0x07, # "7"
"8" : 0x7f, # "8"
"9" : 0x6f, # "9"
"?" : 0x53 # "?"
}
CLEAR_DIGIT = 0x7F
POINT_VALUE = 0x80
DIGIT_ADDR = [0x00, 0x02, 0x06, 0x08]
COLON_ADDR = 0x04
HT16K33_BLINK_CMD = 0x80
HT16K33_BLINK_DISPLAYON = 0x01
HT16K33_BLINK_OFF = 0x00
HT16K33_BLINK_2HZ = 0x02
HT16K33_BLINK_1HZ = 0x04
HT16K33_BLINK_HALFHZ = 0x06
HT16K33_SYSTEM_SETUP = 0x20
HT16K33_OSCILLATOR = 0x01
HT16K33_BRIGHTNESS_CMD = 0xE0
HT16K33_BRIGHTNESS_HIGHEST = 0x0F
HT16K33_BRIGHTNESS_DARKEST = 0x00
# Maximum decimal value that can be displayed on 4 digit Hex Display
HT16K33_MAX_VALUE = 9999
class HT16K33():
""" Class to manage a HT16K33 I2C display """
# Class variables
bus = None
address = None
command = None
def __init__(self, bus, address=0x70, blink=HT16K33_BLINK_OFF, brightness=HT16K33_BRIGHTNESS_HIGHEST):
""" Initialize class variables; Set up display; Set display to blank """
# Initialize class variables
self.bus = bus
self.address = address
self.command = f"/usr/sbin/i2cset -y {bus} {address}"
# Set up display
self._setup(blink, brightness)
# Set display to blank
self.blank()
def _setup(self, blink, brightness):
"""Initialize the display itself"""
if self.command:
os.system(f"{self.command} {HT16K33_SYSTEM_SETUP | HT16K33_OSCILLATOR}")
os.system(f"{self.command} {HT16K33_BLINK_CMD | blink | HT16K33_BLINK_DISPLAYON}")
os.system(f"{self.command} {HT16K33_BRIGHTNESS_CMD | brightness}")
def encode(self, data, double_point=False):
"""Encode data to TM1637 format."""
ret_val = 0
try:
if data != CLEAR_DIGIT:
ret_val = HEX_DIGITS[data] + POINT_VALUE if double_point else HEX_DIGITS[data]
except ValueError:
raise ValueError("Digit value must be between 0 and 15.")
return ret_val
def set_digit(self, digit_number, data, double_point=False):
"""Update the given digit of the display."""
if self.command:
os.system(f"{self.command} {DIGIT_ADDR[digit_number]} {self.encode(data, double_point)}")
def set_colon(self, enable):
"""Set the colon on the display."""
if self.command:
os.system(f"{self.command} {COLON_ADDR} {0x02 if enable else 0x00}")
def set_digit_raw(self, digit_number, data):
"""Update the given digit of the display using raw data value"""
if self.command:
os.system(f"{self.command} {DIGIT_ADDR[digit_number]} {data}")
def blank(self):
"""Clear the display to read nothing"""
if self.command:
self.set_colon(False)
for i in range(4):
self.set_digit_raw(i, 0x00)
def clear(self):
"""Clear the display to read '0000'"""
if self.command:
self.set_colon(False)
self.update(0)
def update(self, value):
"""This function will clear the display and then set the appropriate digits."""
if not 0 <= value <= HT16K33_MAX_VALUE:
raise ValueError("Value is not between 0 and 9999")
self.set_digit(3, value % 10)
self.set_digit(2, (value // 10) % 10)
self.set_digit(1, (value // 100) % 10)
self.set_digit(0, (value // 1000) % 10)
scoring_logic
Python"""
--------------------------------------------------------------------------
Scoring_logic.py
--------------------------------------------------------------------------
License:
Copyright 2023 Mason Melendez
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------
Logic to use IR breakbeam to detect a made basket, implementing the score note
"""
import Adafruit_BBIO.GPIO as GPIO
import time
import digitalio
from BangNote import setup as bang_setup, bang
from ht16k33_project import HT16K33
def scoring_logic_timer(display, stop_event):
bang_setup()
score = 0
ir_pin = digitalio.DigitalInOut(board.P2_2)
ir_pin.switch_to_input(pull=digitalio.Pull.UP)
while not stop_event.is_set():
if ir_pin.value == False:
# A basket is made, increment the score by 2
score += 2
# Update the HT16K33 Display with the new score
display.update(score)
# Play sound or perform other actions for a made basket
bang()
time.sleep(0.0001)
else:
time.sleep(0.0001)
Main_Script
Python"""
--------------------------------------------------------------------------
Main_Script
--------------------------------------------------------------------------
License:
Copyright 2023 Mason Melendez
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------
"""
# Import necessary libraries
import time
import threading
import digitalio
import board
import Adafruit_BBIO.PWM as PWM
import adafruit_character_lcd.character_lcd as characterlcd
from scoring_logic import scoring_logic_timer, bang_setup
from ht16k33_project import HT16K33
from BangNote import bang
# LCD setup
lcd_columns = 16
lcd_rows = 2
lcd_rs = digitalio.DigitalInOut(board.P2_18)
lcd_en = digitalio.DigitalInOut(board.P2_17)
lcd_d7 = digitalio.DigitalInOut(board.P2_20)
lcd_d6 = digitalio.DigitalInOut(board.P2_19)
lcd_d5 = digitalio.DigitalInOut(board.P2_24)
lcd_d4 = digitalio.DigitalInOut(board.P2_22)
lcd = characterlcd.Character_LCD_Mono(
lcd_rs, lcd_en, lcd_d4, lcd_d5, lcd_d6, lcd_d7, lcd_columns, lcd_rows)
# GPIO setup
button_pin = digitalio.DigitalInOut(board.P2_3)
ir_pin = digitalio.DigitalInOut(board.P2_2)
ir_pin.switch_to_input(pull=digitalio.Pull.UP)
# Initialize the HT16K33 Display
display = HT16K33(1, 0x70)
# Function to display blinking message until the button is pressed
def wait_for_button_press(message):
lcd.clear()
while button_pin.value:
lcd.message = message
time.sleep(0.5)
lcd.clear()
time.sleep(0.5)
# Function to display countdown on LCD
def countdown(t, message):
while t:
mins, secs = divmod(t, 60)
timer = '{:02d}:{:02d}'.format(mins, secs)
lcd.message = timer
time.sleep(1)
t -= 1
lcd.clear() # Clear the LCD after the countdown is complete
lcd.message = message
time.sleep(1)
lcd.clear()
# Start the main script
if __name__ == '__main__':
# Wait for button press to start the game
wait_for_button_press("Press button\n to start")
# Initialize components
bang_setup()
# Start the score at 0
score = 0
# Event to signal the scoring logic to stop after 20 seconds
stop_scoring_event = threading.Event()
# Start the scoring logic in a separate thread
scoring_thread = threading.Thread(target=scoring_logic_timer, args=(display, stop_scoring_event))
scoring_thread.start()
try:
# Initial countdown of 5 seconds
countdown(5, "Begin!")
# Sets game timer to 20 seconds
t = 20
while t>0 and not stop_scoring_event.is_set():
mins, secs = divmod(t, 60)
timer = '%02d:%02d' % (mins, secs)
lcd.message = timer
time.sleep(0.05) #Sets how long to wait in between checking if IR break beam is in tact
t -= 0.05
if ir_pin.value == False:
# Add score
score += 2
# Update Display
display.update(score)
# Play sound
bang()
if t == 0:
# Signal the scoring thread to stop after 45 seconds
stop_scoring_event.set()
# Wait for the scoring thread to finish
scoring_thread.join()
break # Exit the loop right before displaying "Game over!"
lcd.clear() # Clear the LCD after the countdown is complete
lcd.message = "Game over!"
time.sleep(3) # Display "Game over!" for 3 seconds
lcd.clear()
display = HT16K33(1, 0x70)
except KeyboardInterrupt:
pass # Handle KeyboardInterrupt (Ctrl+C) gracefully
Comments
Please log in or sign up to comment.