Team: Micheal Nguyen and Jason Perez

Abstract
Our goal was to create an interactive game where a user could use marble projectiles to shoot at targets and score points, in an engaging or addictive fashion. eyeDrop Attack is an engaging, on-the-floor game that allows players to drop, throw or otherwise launch a marble projectile at any of the six eyes of the monster. Players score points based on how long the “eyes” or limit switches are pressed, which incentivizes strategies that target multiple eyes or target a single eye for a continuous period of time.
Objectives
While our original goal was to create an arcade-style shooter which uses a horizontal launch trajectory to target multiple vertical targets, we ended up simplifying our vision to a top-to-bottom or horizontal target surface, to improve the feasibility of the project. By simplifying our project we were able to focus our efforts and achieve a functional marble return system, LCD text and score display, and target movement system capable of 360-degree rotation.
Implementation
Our final implementation consisted of the hit registration system, the rotating movement system, and the control and scoring system. The hit registration system was designed based on a limit switch and hinge combo. This system proved difficult to implement because the original hinges we used had very loose tolerances which introduced more errors and made hit registration inconsistent. The newer metal hinges we purchased had much tighter tolerances and produced much better results.

The rotational motion system produced the most interesting challenge as we wanted to produce a system with full 360-degree rotation and electronic components on both the rotating piece and the box. We were able to solve this issue using a slip ring which allowed us to connect our rotating switches to our fixed microcontroller. This slip ring needed to be at the axis of rotation which presented an issue as this would take the place of any axle-based system we might have otherwise used. Garth’s open hub design helped us solve this problem, by allowing us to create a rotational movement system with an open center, allowing us to pass wires through the center.
We controlled the motion system by tuning the speed of the motor to suit the state of the game. In demo mode, the motor will follow a simple sinusoidal wave. During play, the motor’s speed is the sum of two sinusoidal waves of differing periods creating a randomized effect. After a hit, the motor will operate with a higher speed floor, and a shorter sinusoidal period to create the illusion that the monster is recoiling from the hit.
The electronics system also included the scoring system. The scoring system increments the score based on how long the switches on the eye have been pressed. This score is then displayed on the LCD display. The system also supports multiple games as the player can end the game by pressing the “Start/Stop” button to enter demo mode, and then pressing the “Start/Stop” button again to enter play mode once again. This action will set the score to zero.

Outcomes

Our main successes were the use of the slip ring, switch mounting, and the long-running nature of our project. The early incorporation of the slip ring in our design allowed us to manage our wiring in one place and, most importantly, gave us full rotation on a single axis. The switch mounting also introduced another degree of freedom by allowing us to fine-tune the hit recognition system. The mounting was critical in determining which position would offer the most consistent actuation. Another important success was the persistence of our project. The centralized design and mounting ensured the rotating mechanism was able to keep moving for relatively long periods of time.
Unexpected successes arose when new players interacted with our project. Many of our users were captivated by both the crazy visuals and bizarre mechanical behavior of our machine. Their experience was enhanced through the self-imposed difficulty of our game as well as the feedback from scoring. Over time, users developed their own game modes, one of which was a co-op mode of sorts that involved various users dropping marbles.
Future Work
- Smoother and more accessible return system (possibel skeeball implementation)
- Sound from speakers with sound effects for hits and music
- More game modes
Contributions
Jason Perez – Helped come up with ideas during brainstorming, introduced hinges as a component to the target system, developed central rotator piece through CAD, developed switch mounting through CAD, programmed initial code for testing switches-motor-LCD, graphic design/theme of the project
Michael Nguyen – created Box Full Assembly CAD model, converted code to polling states to allow for simulated multitasking, motion profiled rotator for demo, play, and freakout states, designed gearbox and debugged LCD display.
Media
Video
Gallery
Citations
Motor Sample Code:
https://courses.ideate.cmu.edu/16-223/f2021/text/code/pico-motor.html#id7
Hub:
https://courses.ideate.cmu.edu/16-223/f2021/text/mechanism/open-hub.html
Code + CAD
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 | # dual_spin.py # # Raspberry Pi Pico - DC motor motion demo # # Demonstrates operating two DC motors driven by a DRV8833. # # This assumes a Pololu DRV8833 dual motor driver has been wired up to the Pico as follows: # Pico pin 24, GPIO18 -> AIN1 # Pico pin 25, GPIO19 -> AIN2 # Pico pin 26, GPIO20 -> BIN2 # Pico pin 27, GPIO21 -> BIN1 # any Pico GND -> GND # DRV8833 carrier board: https://www.pololu.com/product/2130 ################################################################ # CircuitPython module documentation: ################################################################################ # print a banner as reminder of what code is loaded print ( "Starting dual_spin script." ) # load standard Python modules import math, time # load the CircuitPython hardware definition module for pin definitions import board # load the CircuitPython pulse-width-modulation module for driving hardware import pwmio import busio import adafruit_ht16k33.segments from digitalio import DigitalInOut, Direction, Pull #-------------------------------------------------------------------------------- # Class to represent a single dual H-bridge driver. class DRV8833(): def __init__( self , AIN1 = board.GP18, AIN2 = board.GP19, BIN2 = board.GP20, BIN1 = board.GP21, pwm_rate = 20000 ): # Create a pair of PWMOut objects for each motor channel. self .ain1 = pwmio.PWMOut(AIN1, duty_cycle = 0 , frequency = pwm_rate) self .ain2 = pwmio.PWMOut(AIN2, duty_cycle = 0 , frequency = pwm_rate) self .bin1 = pwmio.PWMOut(BIN1, duty_cycle = 0 , frequency = pwm_rate) self .bin2 = pwmio.PWMOut(BIN2, duty_cycle = 0 , frequency = pwm_rate) def write( self , channel, rate): """Set the speed and direction on a single motor channel. :param channel: 0 for motor A, 1 for motor B :param rate: modulation value between -1.0 and 1.0, full reverse to full forward.""" # convert the rate into a 16-bit fixed point integer pwm = min ( max ( int ( 2 * * 16 * abs (rate)), 0 ), 65535 ) if channel = = 0 : if rate < 0 : self .ain1.duty_cycle = pwm self .ain2.duty_cycle = 0 else : self .ain1.duty_cycle = 0 self .ain2.duty_cycle = pwm else : if rate < 0 : self .bin1.duty_cycle = pwm self .bin2.duty_cycle = 0 else : self .bin1.duty_cycle = 0 self .bin2.duty_cycle = pwm #-------------------------------------------------------------------------------- # Create an object to represent a dual motor driver. print ( "Creating driver object." ) driver = DRV8833() speed = 0.65 #LED led = DigitalInOut(board.LED) led.direction = Direction.OUTPUT led.value = True #7 Segment Display i2c = busio.I2C(scl = board.GP5, sda = board.GP4) display = adafruit_ht16k33.segments.Seg7x4(i2c) #Switches switchStart = DigitalInOut(board.GP9) switchStart.pull = Pull.DOWN switchStart.direction = Direction.OUTPUT wasPressed = False switch0 = DigitalInOut(board.GP10) switch0.pull = Pull.DOWN switch0.direction = Direction.OUTPUT switch1 = DigitalInOut(board.GP11) switch1.pull = Pull.DOWN switch1.direction = Direction.OUTPUT switch2 = DigitalInOut(board.GP12) switch2.pull = Pull.DOWN switch2.direction = Direction.OUTPUT switch3 = DigitalInOut(board.GP13) switch3.pull = Pull.DOWN switch3.direction = Direction.OUTPUT switch4 = DigitalInOut(board.GP14) switch4.pull = Pull.DOWN switch4.direction = Direction.OUTPUT switch5 = DigitalInOut(board.GP15) switch5.pull = Pull.DOWN switch5.direction = Direction.OUTPUT #States? demoMode = True #-------------------------------------------------------------------------------- # Begin the main processing loop. This is structured as a looping script, since # each movement primitive 'blocks', i.e. doesn't return until the action is # finished. class Game: def __init__( self ): self .score = 0 self .scoreScale = 5 self .motorTime = 0.01 self .nextMotorTime = 0.01 self .demoMode = True self .wasReleased = True self .startTime = time.time() self .hurtTime = 0 def getSpeed( self ,scale): currTime = time.time() - self .startTime speed = math.sin(currTime / scale) return min ( max ( abs (speed), 0.6 ), 0.8 ) * (speed / ( abs (speed) + 0.001 )) def pollMainSwitches( self ): if not self .demoMode: if switch0.value or switch1.value or switch2.value or switch3.value or switch4.value or switch5.value: self .score + = self .scoreScale self .hurtTime = time.time() + 1 def pollMotors( self ): currTime = time.time() if (currTime > = self .nextMotorTime): self .nextMotorTime = currTime + self .motorTime if ( self .demoMode): speed = self .getSpeed( 2 ) driver.write( 1 , speed) elif (currTime < = self .hurtTime): speed = 4 * self .getSpeed( 0.001 ) driver.write( 1 , speed) else : speed = self .getSpeed( 2 ) driver.write( 1 , speed) def pollStart( self ): if not switchStart.value: self .wasReleased = True if self .wasReleased and switchStart.value: self .demoMode = not self .demoMode led.value = False self .wasReleased = False def pollDisplay( self ): if self .demoMode: #Press display.set_digit_raw( 0 , 0b01110011 ) #P display.set_digit_raw( 1 , 0b01010000 ) #r display[ 2 ] = "e" display[ 3 ] = "5" display.scroll( 1 ) display[ 3 ] = "5" display.show() display.scroll( 1 ) display[ 3 ] = " " display.show() display.scroll( 1 ) display.set_digit_raw( 3 , 0b01111000 ) #t display.show() display.scroll( 1 ) display.set_digit_raw( 3 , 0b01011100 ) #o display.show() display.scroll( 1 ) display[ 3 ] = " " display.show() display.scroll( 1 ) display[ 3 ] = "5" display.show() display.scroll( 1 ) display.set_digit_raw( 1 , 0b01111000 ) display.show() display.scroll( 1 ) display[ 3 ] = "a" display.show() display.scroll( 1 ) display.set_digit_raw( 3 , 0b01010000 ) display.show() display.scroll( 1 ) display.set_digit_raw( 1 , 0b01111000 ) display.show() display.scroll( 1 ) display[ 3 ] = " " #Start #display[0] = "5" #display.set_digit_raw(1, 0b01111000) #display[2] = "A" #display.set_digit_raw(3, 0b01010000) #display.set_digit_raw(1, 0b01111000) else : display. print ( "%04d" % self .score) print ( "Starting main script." ) scale = 2.0 startTime = time.time() game = Game(); while True : currTime = time.time() - startTime led.value = game.demoMode game.pollMotors() game.pollDisplay() game.pollMainSwitches() game.pollStart(); |
CAD Files:
https://drive.google.com/file/d/1EFqiocY5mWaKRPtHlE2MzSBPyFFcaLiz/view?usp=sharing
Leave a Reply
You must be logged in to post a comment.