# Interactive Tabletop Basketball Arcade Game 互動式桌上型籃球街機
Made by Jay Zhou
# Overview
This device simulates the gameplay of a basketball arcade, allowing users to shoot basketballs by selecting the force, while the system keeps track of your score.
# The appearance of the device.


# Required Components
## Software
* Python 3.7
* SPI bus
## Hardware
* Raspberry Pi 4B
* SD Card 32GB
* GP2Y0A51SK0F Sharp Infrared Distance Sensor 2-15cm
* DC flat moter
* L298N
* MCP3008
* Breadboard
* jumper cable
## Tool
* PlayWood
* Glue gun
* double-sided tape
* AA batteries
* AA battery holder
* Metal rod
* Wheel
## Getting Started with Raspberry Pi
### Reference :
[https://randomnerdtutorials.com/getting-started-with-raspberry-pi/](https://)
## Setting up GP2Y0A51SK0F with MCP3008
### GP2Y0A51SK0F
GP2Y0A51SK0F is a distance measuring sensor unit, composed of an integrated combination of PSD (position sensitive detector) , IR-LED (infrared emitting diode) and signal processing circuit. The variety of the reflectivity of the object, the environmental temperature and the operating duration are not influenced easily to the distance detection because of adopting the triangulation method. This device outputs the voltage corresponding to the detection distance. So this sensor can also be used as a proximity sensor


The distance sensor has only three connections: red (5V), black (GND) and white, which is the data pin and connected to the MCP3008 ADC.
### MCP3008
The MCP3008 chip is a 10-bit digital-to-analog converter that reads analog signals and sends them to a microcontroller via SPI communication protocol (SPI signals that the Raspberry Pi can read).
The MCP3008 comes with 16 pins. Half of those pins are analog inputs that you can use to connect to analog devices. The other 8 pins connect to the Raspberry Pi GPIOs.

The 8 pins at the left side of the MCP3008 are eight different analog channels. You can connect up to 8 analog devices using those channels. The pins on the right side connect to the Raspberry Pi.
The easiest way to wire an MCP3008 chip to the Raspberry Pi is using a breadboard. Place the MCP3008 chip in the middle of the breadboard. Then, connect it as shown in the following table:
| MCP3008 Pin | Raspberry Pi |
| ----------- |:--------------------------------------------- |
| 1 | Analog Output(potentiometer or analog sensor)|
| 9 | GND |
| 10 | GPIO 8 |
| 11 | GPIO 10 |
| 12 | GPIO 9 |
| 13 | GPIO 11 |
| 14 | GND |
| 15 | 3.3V |
| 16 | 3.3V |
**The whole circuit looks like this:**


## Software for Reading the Distance
**To control the MCP3008, the SPI bus must be activated. This works as follows:**
`sudo raspi-config`
„3 Interface Options“ -> „I4 SPI“ -> „Yes“.
**After that, you have to confirm the restart.**
**In some cases, the module (spi-bcm2708) must also be entered in the / etc / modules file. For that just call**
`sudo nano /etc/modules`
**and add the following line at the end (if it does not exist):**
`spi-bcm2807`
**Now the spidev library can be installed, if it has not already been done:**
```
sudo apt-get install git python-dev
git clone git://github.com/doceme/py-spidev
cd py-spidev/
sudo python setup.py install
```
**Now that we have all the needed packages installed.**
## Code
**This code measures the central position of the basketball hoop based on the voltage-distance correlation from the GP2Y0A51SK0F datasheet. When the voltage exceeds 1.0, it's recognized as a scored goal, and the goal count increases by one.**
---
```
import spidev
import time
def setup_spi(channel):
spi = spidev.SpiDev()
spi.open(0, 0)
spi.max_speed_hz = 1000000
return spi
def read_channel(spi, channel):
val = spi.xfer2([1, (8 + channel) << 4, 0])
data = ((val[1] & 3) << 8) + val[2]
return data
if __name__ == "__main__":
spi = setup_spi(0)
count = 0
try:
while True:
sensor_value = read_channel(spi, 0)
voltage = (sensor_value / 1023.0) * 3.3
print("Voltage:", voltage)
if voltage > 1.0:
count += 1
print("Count:", count)
time.sleep(0.05)
except KeyboardInterrupt:
spi.close()
print("Measurement stopped by User")
```
---
## To control a DC motor using the L298N motor driver
### L298N
The L298N driver module is capable of directly driving two 3-30V DC motors. It also provides a 5V output interface for powering 5V microcontroller circuits. It supports 3.3V MCU control, facilitating easy control of DC motor speed and direction. Additionally, it can control a single 2-phase stepper motor

### DC moter

**The whole circuit looks like this:**

## Code
**This code uses Raspberry Pi's GPIO to control a motor and utilizes tkinter to create a simple user interface. The program allows the user to set the motor speed, corresponding to the force of shooting a basketball. Subsequently, it starts the motor's operation, which lasts for a specific duration, during which basketball shots can be continuously taken. After this time, the motor automatically stops. Users have the option to choose whether to continue the game or close the program.**
```
---
import RPi.GPIO as GPIO
from tkinter import *
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.cleanup()
in1 = 23
in2 = 24
en = 25
GPIO.setup(in1, GPIO.OUT)
GPIO.setup(in2, GPIO.OUT)
GPIO.setup(en, GPIO.OUT)
GPIO.output(in1, GPIO.LOW)
GPIO.output(in2, GPIO.LOW)
p = GPIO.PWM(en, 1000)
motor_running = False
speed_pwm = 0
def set_speed_window():
global speed_pwm
set_speed_root = Tk()
set_speed_root.title("Set Speed")
def set_speed():
global speed_pwm
speed = int(speed_entry.get())
if 50 <= speed <= 85:
speed_pwm = speed
set_speed_root.destroy()
run_motor()
else:
speed_label.config(text="Speed should be between 50 and 85")
speed_label = Label(set_speed_root, text="Enter speed (50-85):")
speed_label.pack()
speed_entry = Entry(set_speed_root)
speed_entry.pack()
set_speed_button = Button(set_speed_root, text="Set Speed", command=set_speed)
set_speed_button.pack()
set_speed_root.mainloop()
def run_motor():
global motor_running
global speed_pwm
motor_running = True
GPIO.output(in1, GPIO.HIGH)
GPIO.output(in2, GPIO.LOW)
motor_running_root = Tk()
motor_running_root.title("Motor Running")
def stop_motor():
global motor_running
motor_running = False
p.ChangeDutyCycle(0)
motor_running_root.destroy()
continue_window()
p.start(25)
p.ChangeDutyCycle(speed_pwm)
motor_output = Label(motor_running_root, text="Motor running for 30 seconds...")
motor_output.pack()
stop_button = Button(motor_running_root, text="Stop Motor", command=stop_motor)
stop_button.pack()
motor_running_root.after(30000, stop_motor)
motor_running_root.mainloop()
def continue_window():
continue_root = Tk()
continue_root.title("Continue")
def continue_game():
continue_root.destroy()
set_speed_window()
def close_game():
continue_root.destroy()
GPIO.cleanup()
continue_label = Label(continue_root, text="Do you want to continue playing?")
continue_label.pack()
continue_button = Button(continue_root, text="Continue", command=continue_game)
continue_button.pack()
close_button = Button(continue_root, text="Close", command=close_game)
close_button.pack()
continue_root.mainloop()
set_speed_window()
```
## Shooting device
utilizes a DC motor attached to a wheel, accompanied by an adjacent wheel capable of following and rotating. Shots are propelled through the rotation and friction of these wheels.



# Final code
```
import spidev
import RPi.GPIO as GPIO
from tkinter import *
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.cleanup()
score = 0
def setup_motor():
global in1, in2, en, p
in1 = 23
in2 = 24
en = 25
GPIO.setup(in1, GPIO.OUT)
GPIO.setup(in2, GPIO.OUT)
GPIO.setup(en, GPIO.OUT)
GPIO.output(in1, GPIO.LOW)
GPIO.output(in2, GPIO.LOW)
p = GPIO.PWM(en, 1000)
p.start(25)
return p
def run_motor(p, speed):
GPIO.output(in1, GPIO.HIGH)
GPIO.output(in2, GPIO.LOW)
p.ChangeDutyCycle(speed)
def stop_motor(p):
p.ChangeDutyCycle(0)
def setup_spi(channel):
spi = spidev.SpiDev()
spi.open(0, 0)
spi.max_speed_hz = 1000000
return spi
def read_channel(spi, channel):
val = spi.xfer2([1, (8 + channel) << 4, 0])
data = ((val[1] & 3) << 8) + val[2]
return data
def start_game():
def on_start():
speed = int(speed_entry.get())
if 60 <= speed <= 85:
start_window.destroy()
if 'game_window' in globals() and game_window.winfo_exists():
game_window.destroy()
run_game(speed)
else:
speed_label.config(text="Speed should be between 60 and 85")
start_window = Tk()
start_window.title("Basketball IoT Game")
speed_label = Label(start_window, text="Enter speed (60-85):")
speed_label.pack()
speed_entry = Entry(start_window)
speed_entry.pack()
set_speed_button = Button(start_window, text="Set Speed", command=on_start)
set_speed_button.pack()
start_window.mainloop()
def run_game(speed):
global score
global game_window
motor = setup_motor()
spi = setup_spi(0)
def end_game():
global p, game_window
stop_motor(p)
if 'p' in globals():
p.stop()
del p
GPIO.cleanup()
game_window.destroy()
end_window = Tk()
end_window.title("Game Over")
score_label = Label(end_window, text=f"Score: {score}")
score_label.pack()
continue_button = Button(end_window, text="Continue", command=start_game)
continue_button.pack()
end_window.mainloop()
def update_score():
global score, game_window
sensor_value = read_channel(spi, 0)
voltage = (sensor_value / 1023.0) * 3.3
if voltage > 1.0 and game_window:
score += 1
score_label.config(text=f"Score: {score}")
if game_window:
game_window.after(50, update_score)
score = 0
game_window = Tk()
game_window.title("Basketball Game")
score_label = Label(game_window, text="Score: 0")
score_label.pack()
game_window.after(30000, end_game) # 30 seconds game
run_motor(motor, speed)
update_score()
game_window.mainloop()
start_game()
```
# Demo
https://youtu.be/33Z_vJ0AVSs
# Reference
* https://randomnerdtutorials.com/raspberry-pi-analog-inputs-python-mcp3008/
* https://randomnerdtutorials.com/getting-started-with-raspberry-pi/
* https://tutorials-raspberrypi.com/infrared-distance-measurement-with-the-raspberry-pi-sharp-gp2y0a02yk0f/
* https://www.electronicshub.org/raspberry-pi-l298n-interface-tutorial-control-dc-motor-l298n-raspberry-pi/
* https://pinout.xyz/pinout/pin22_gpio25/
* https://global.sharp/products/device/lineup/data/pdf/datasheet/gp2y0a51sk_e.pdf