Introduction: Arduino Pinball Machine That Plays Itself!
"A pinball machine that plays itself, doesn't that take all the fun out of it?" I hear you ask. Maybe if you're not into autonomous robots it might. Me, however, I'm very much all about building robots that can do cool stuff, and this one does some pretty cool stuff.
This project was built as a senior design project for Kennesaw State University, and was a literal childhood dream come true for me to build.
The features include a working score system that tracks how many points you get, a multiball plinko machine, and an autonomous activation switch on the front that you can flip on-the-fly. There is an above-mounted USB camera that is constantly detecting the position of the flippers and the position of the pinball during play and it makes decisions based on their relative differences. More pictures of the project exist here!
While you may not be able (or even want to) recreate the project exactly, I hope this gives you inspiration or a starting point to make awesome stuff.
So, prepare yourself and...
Let's Make Robots!
Obviously, there's lots of supplies involved in this project, and I don't think I can list every single piece, nor do I think it would be useful. However, I do want to provide a list of major pinball components, and tools needed to build this project. In later sections, I will try to have a more detailed list for specific components.
- Access to a CNC and/or Laser cutter
- Dremel & sandpaper
- Soldering Iron
- 3D printer (depending on your machine)
- Linux Computer
- USB Camera
- Lots of 22 / 24 AWG wire
- Lots of heat shrink for the wires
- 3/4 Inch plywood (we used Baltic birch) - 2x 4x8 sheets
- A nice power supply - Like this one!
- Buck Converter (Light Power) - Like this one!
Pretty much all the pinball components can be bought on Pinball Life.
- Left and Right Flipper assembly
- 2x Flipper bats
- 2x Flipper buttons
- 2x Leaf switches
- Pop Bumper Assembly
- 2x Slingshot assemblies
- At least 6x star posts for the slingshots
- At least 2x 2" rubber bands for the star posts
- Launcher mechanism
- As many #44 bayonet-style lights and mounting brackets as your machine needs
- As many playfield inserts as your machine needs
- As many spinners as your machine needs
- As many rollover switches as your machine needs
- As many stand-up targets as your machine needs
And, of course, an Arduino Mega!
Step 1: Research How It Works
The first step in building anything is to do some light research on how the individual parts of the thing work together. I am assuming you will have at least a slight understanding of electrical components, but even if you don't I hope this still helps.
General Pinball Design:
For general help when thinking about a pinball machine, these links should provide you with a great understanding.
- Pinball Design, Start to Finish - https://howtobuildapinballmachine.wordpress.com
- Concise pinball component descriptions - https://www.topic.com/anatomy-of-a-pinball-machine
- Great Instructable with nice animations - https://www.instructables.com/id/Making-a-Pinball-Machine/
Most of the pinball components have an electro-mechanical process that actuates them.
- Flippers - https://www.youtube.com/watch?v=1YitMJS0jbY
- Pop Bumper - https://www.youtube.com/watch?v=0VWdRp2suv4
- Slingshots - https://pinballmakers.com/wiki/index.php?title=Slingshots
- Transistor Theory - https://learn.sparkfun.com/tutorials/transistors/applications-i-switches
Mechanical Pinball Design:
This section includes CAD models, woodworking tips, and other useful mechanical help
- CAD models by pinballmakers.com - https://pinballmakers.com/wiki/index.php?title=Files_Section
- CAD models made by our team - https://github.com/Tdoe4321/AutoPinball/tree/master/CAD_Models
- Wood and Acrylic Milling - https://www.youtube.com/watch?v=oUE3zWnZbTo&feature=youtu.be
- Making Dovetails - https://youtu.be/viT3ZGAlzNw
Software & Autonomy Design:
This section has links to different algorithms and projects that could be helpful when making something autonomous.
- A similar project's github repo - https://github.com/jherrm/flipbot/blob/master/capture_pinball.py
- Smoothing Algorithms (for velocity / position tracking) - http://18.104.22.168/applets/AppletSmooth/Appl_Smooth2.html
- Speeding up arduino hardware ROS bridge (if needed) - http://wiki.ros.org/rosserial_arduino/Tutorials/NodeHandle%20and%20ArduinoHardware
Step 2: Design Your Machine on Paper
So this may seem like a simpler task at first, and if you've been thinking about it for a long time, maybe it is. However, for one reason or another, this can turn out to be quite a difficult thing to accomplish.
You may have space constraints that you did not account for at first, or maybe some of the shots you were thinking about are just impossible for your flippers to hit. All of these things need to be working around in your head and on paper before you go out and spend time and money on a design that doesn't work.
For our team, we broke it down into few test boards on really cheap plywood before actually going through and milling out the final playfield. We also went through lots of design iterations and were constantly changing what the machine looked like, but each step we took got us a little closer to the final product.
So, learn from our mistakes and use these helpful tips:
- Draw on paper (or a whiteboard or whatever) before moving to 3D modeling.
- Plan for making errors in your milling, have "modular" features that can be taken out and put back in.
- Don't reinvent the wheel, check out popular games and how they structure their playfield.
- The dream in your head won't be exactly what ends up in front of you, but take what's there and run with it.
Step 3: Design You Machine in SolidWorks
This part if fully up to you obviously, but I will go through some of the challenges and things that we did when making our machine.
As mentioned earlier, we designed all of our parts in solidworks for this project, so feel free to use our models here - https://github.com/Tdoe4321/AutoPinball/tree/master/CAD_Models
The mechanical design for the AutoPinball machine consists of two major subsets, the playfield proper and the stand. The playfield is standard sized - 20.25” x 42.00” - and is made from ¾” baltic birch plywood, the material of choice for the final design. The first major design considerations involved the positioning of COTS components on the playfield in addition to custom-designed boundaries. For this explanation, “components” are any electric or non-electric COTS parts in which the ball interacts while “boundaries” constitute static custom-made wood or acrylic surfaces that may alter the trajectory of the ball. The components and boundaries are positioned to provide maximum maneuverability for the ball as it traverses the playfield with the boundaries directing the ball to desired components. Generally, the regions are constructed such that a ball may transverse from any one region to any other adjacent region with little to no impedance depending on approach angle and speed. For instance, a ball may launch from the left flipper in the “L. In/Out Lanes” region and enter any other region non-including the launch lane. If the ball enters “Ramp & R. Orbit”, it may move counterclockwise around the orbit into the “Center Lane”, interact with the pop bumpers and be ejected into the center lane, or complete the orbit through the “L. Orbit and Y-Lane” to then return to the left flipper.
The playfield includes a second level made with a combination of 2”-diameter acrylic tubing and 3D printed adaptors. The second level serves two cardinal functions. Firstly, the second level serves as a medium to transport the ball from the upper playfield directly to the left flipper inlane. The predictability of the final deposited location of the ball makes the second level a consistent pathway in which to facilitate multiball, its second primary function. When the roll-over switches on the ramp and left inlane are triggered back-to-back, a servo releases two balls down the Plinko machine which are deposited into one of two tubes mounted above the second level to deconflict with ramp shots. Therefore, the tubes feed the second level, and hence the left inlane, during the multiball mode.
The ¾” plywood thickness is selected to provide adequate rigidity to the project and to allow for greater fastener engagement in load-bearing joints. Pocket hole geometry is an alternate method to the common butt joint in woodworking in which two end pieces of wood are joined at right angles. The pocket joint creates a fifteen-degree hole through one side face into another side face, yielding a superior joint in terms of strength as no end faces are used. Pocket holes, while allowable with ½” thick wood, are most common and efficient for ¾” and the 1 ¼” pocket screws required for ¾” thick wood are much more common. Baltic birch is selected as the material type as it is high quality with few knots and impurities, is a hardwood and thus is rigid and resistant to damage, is laser friendly (for engraving), and generally is preferred by woodworkers for heavier projects.
Two major consequences are derived from the selection of ¾” thick plywood. The first is that most class four lasers do not exceed a power rating of 150 W which itself is not enough power to cut through hardwood of such thickness. Therefore, the project must be CNC’d with a five axis machine. The second consequence is that the COTS pinball components, such as the solenoids, roll-over switches, LED’s, stand-up targets, etc., are designed for ½” panel mounting. As such, the playfield is required to be CNC’d from the bottom face with ¼” recesses end milled for the components. The requirement for accurate mounting features and recesses adds another layer of precision to the project that is solved by the generation of highly accurate CAD models for each of the components.
The stand serves as a mounting apparatus for the playfield and houses the custom electronics. The electronics mount directly to the baseboard with extended wiring harnesses reaching the underside of the playfield. The electronics are visible from the sides of the stand through viewing windows. Additionally, the stand allows for variable pitch adjustment for the playfield via removable pins from the side. Starting at zero degrees, holes are included at each two-degree increment to a maximum of an eight-degree incline. A higher playfield gradient yields a faster-paced game with increased difficulty.
Step 4: CNC or Laser Cut Your Stand and Playfield
While the milling of your stand and playfield do not require a CNC or Laser, it is highly recommended that you use one when cutting the wood for your project. Doing it by hand is incredibly difficult and not nearly as precise as you would like it to be for your end product.
We had access to a large 5-axis CNC, that that is how pretty much all professional games are made these days. If you can't mill it on a CNC though, don't worry! Lots of our parts had to be adjusted after the fact and it's fairly easy to cover up any mistakes with some wood putty.
Step 5: Mount All Components to Playfield & Assemble Stand
Once your playfield has been milled out and your components have all been ordered, it's time to put all the pieces together. We used a pocket hole method to fasten all of our main stand pieces together for the machine.
As for mounting hardware, you really just need a screw that is less than 1/2 inch long to mount the components. We mainly used #4 wood screws.
It's important to realize here also that some components are going to be mounted to the stand, while others will be mounted to the playfield itself. Things like the flipper buttons, power supply, electrical boards, etc. will all be mounted to the stand directly, while the major pinball components will be mounted to the playfield.
Step 6: Prep Electrical Components
Obviously a pinball machine needs power, how would it turn on all those fancy lights and flip the flippers without it?
There are just a few considerations that need to be though of before wiring everything together.
Most pinball machine's "High Voltage" is in the range of 35v - 48v, depending on the make of the solenoids you buy, and you will want to pick a power supply that can support your coils. Secondly, you will need to think about the "Low Voltage" supply for powering things like lights or other smaller electrical components. We chose a voltage of 6.3v for our Low Voltage, but that's not necessarily set in stone. It depends on what LEDs you buy, the brightness you want, and if you're powering other electrical things with this supply. For simple stuff, 6.3v should work fine.
The power supply we bought did not include a Low Voltage supply, so we had to get a separate board that could power our lights. This is what's known as a buck converter. It takes a high voltage (like 48v) and steps it down to what it's rated for. Most are adjustable, like this one!
While most components require a certain voltage to even function properly, current is something that is "induced". This means that the components that you power are going to draw as much current as they need depending on their resistance. Because of this, it's hard to tell just how much power your machine will need to support.
"Is 100 watts ok? What about 50?" These are the questions that are swirling through your head, and the answer is, it depends. Things can get very burnt very quickly if your components aren't rated for the correct power dissipation. The problem comes in when you realize that these components are pulling LARGE amounts of amperage for a very small amount of time. The flippers pull something crazy like 3-4 amps when they are fired, and with two of them that's around 8 amps. The reason that this is able to to happen has to do with the amount of time the coil is actuated for is really small.
I realize that this isn't super helpful, and I apologize for that. My recommendation is to calculate your "worst case scenario" in terms of current draw and then give a nice factor of safety. Answer the question, "How much current will we draw if everything turns on at once?" Once you have the answer to that question, you should be able to pick out a power supply.
Also, checkout this blog post as it goes into lots of detail and tests.
Step 7: Build I/O Boards (Switch Input)
The switch input board takes care of reading in all the values from the playfield into the arduino. The individual circuit for this is extremely simple, but it needs to be scaled up for lots of inputs.
Because the arduino has an internal pullup resistor, described here, you can wire it as seen above. This makes the wiring really simple, because you don't need to have any extra resistors or voltage exposed. In the arduino code, you need to tell it to use the input_pullup, but that will be covered later.
The biggest thing here is to make sure that you have connectors to each switch in case one needs to be taken out for some reason.
We used standard pin headers as our connectors which made it really easy to plug everything into the arduino at once.
Additionally, you may consider adding some circuitry around your playfield switches to prolong the amount of time that they are "on" from the ball interacting with it. One reason you would do this is if the arduino was too slow in checking the switches and "missed" the switch being triggered. We didn't run into that issue, as all the main loop of the arduino code does is check all the switches connected to it. But, if you want to read more about it, check out this forum post.
Step 8: Build I/O Boards (Light Control)
The base circuit for controlling the lights is similarly pretty easy. The circuit consist of a BJT transistor (2n2222), a few resistors, and the LED you're trying to activate. We use the transistor as a digital "switch" that we can turn on or off, like this! You also should be hooking this up to the 6.3v power supply mentioned from earlier. This will provide a nice bright light and individually addressable LEDs.
The reason you can't just hook the LED directly up to the arduino is because the arduino can't provide enough current for that many lights. It's able to handle a few on its own, but when you scale that up to the size of a pinball machine, it needs some surrounding circuitry.
What we're doing here is using the arduino as a digital switch that turns on or off the BJT that completes the circuit for each LED. This allows us to scale up the number of LEDs we have to as many as our power supply can handle without any issue.
Step 9: Build I/O Boards (Solenoid Control)
Now here is where the circuity gets a little more complicated, but fewer of these circuits need to be created. The general idea is the same here as the LED boards. We want to be able to send a signal from the arduino that is able to turn on/off any solenoid (flippers, slingshots, pop bumpers). Because these components take much more power to actuate than an LED, we need some bigger transistors, MOSFETs.
Here's a component list for one circut:
- 1k resistor
- 10k resistor
- 330 resistor
- IRF44V MOSFET
- 1N4004 Diode
- 47 micro-farad capacitor
First, a bit of background:
Every solenoid need to be hooked up to 48v for them to actuate. A solenoid is basically just a really long wire wound in a circle so that, when current moves through it, an electrical field is created. This field moves a metal rod which flips a flipper, drops a pop bumper, or flicks a slingshot. A really nice video about this can be found here!
Another quick note about solenoids and inductors. Because Inductors cannot change current instantaneously, that creates a problem for us. These solenoids are pushing a lot of current through them when they are active, and all that current needs to go somewhere when we tell it to turn off. It needs a place to dissipate slowly. If you don't have a place for the current to go, it will try to force its way through somewhere which can break components and be very dangerous.
To compensate for this, we use an RC snubber circuit and some diodes. They are both basically doing the same thing - trying to slow down the dissipation of current. If you don't include at least one of these (preferably both) then you will definitely break some MOSFETs.
Now, you might notice that the circuit for the flippers and the other coils is slightly different. That is because, in pinball, the player will sometimes want to hold down the flipper button to keep the flipper actuated for a long time. If you were to use the same "high-power" coil to hold the flippers in the upright position, you would quickly burn up your coils. The second coil in the flipper circuit allows for a fast and powerful initial flip. Once the flip is completed, a mechanical process opens up the EOS switch and forces the current to go through both coils. To see more info on how flipper circuits work, checkout this post.
Step 10: Solder All Components Together
Depending on your game size, soldering could obviously take a lot of time or a short while. Ours took about two days to solder and wire everything in nice orderly bundles. I was actually really happy with how organized it turned out.
We ended up with 5 connectors that would plug into our stack of boards.
- High-power supply for the solenoids
- Connections to the solenoid-specific switches
- Connections to the LEDs
- Connections to the switches
- Some auxiliary power (5v, 48v, etc.)
All of these plugged into a 3D printed connection board that held all of our circuitry stuff. It also made it super easy to diagnose things when we needed to get under the hood. All you had to do was unplug the 5 large connectors, then lift up the machine.
Step 11: Install Dependencies on Computer & Arduino (and a Software Overview)
On this machine, we have the following dependencies that need to be installed on the computer attached to the arduino.
All of the dependency instalation instructions can be found on the github page's README
Once all the dependencies are installed, you will also need to build the project.
Building it is super easy and can also be found on the github page.
While we're talking about software though, now is probably a good time to give a good overview of the different parts of the project. Most of it will be in relation to the above diagram, so it will be helpful to have it handy.
The entire software system relies on the ROS architecture as a back-end for passing messages back and forth. The overview includes a list of class structures that the nodes rely on for a more structured and organized approach.
Four main nodes communicate with each other asynchronously to control the flow of the pinball machine when it is run in autonomous mode. These nodes are Input_Output.ino, track_metal.cpp, run_low_level.py, and GUI.py. When not running in autonomous mode, the node track_metal.cpp may be omitted.
Input_Output.ino is the script uploaded to the Arduino that handles all the literal switching on/off of the pinball components and reading in from any playfield switches. These items include flippers, bumpers, slingshots, lights, switches, stand-up targets, a spinner, a start button, and an autonomy on/off switch. The Input_Output.ino node has two major functions: turning on items on the playfield and relaying the status of switches back to the computer. These two items are completed through the ROS topics.
The track_metal.cpp node takes care of locating the ball in real-time, locating the flippers at set increments, and making a decision of when to flip the flippers. Additionally, the track_metal.cpp node takes in a single input from the Input_Output.ino node of the status of the physical autonomy switch to know when it should be flipping the flippers.
Locating the ball in track_metal.cpp is done in 6 steps:
- Take a picture of the playfield when the flippers are down and there is no ball in the frame.
- Convert that frame to grayscale.
- Compare every new frame to the original frame by using a binary threshold. This is possible because each frame has a single pixel color value that defines it.
- Enlarge anomalies found in the binary comparison using cv::dilate().
- Retrieve a list of contours from this binary comparison using cv::findContours().
- Loop through the list. Any contour’s area that is in-between some threshold will be classified as a ball.
This method for finding pinballs on the playfield has both advantages and disadvantages over attempting to detect circles in the image. The first major advantage with this method is speed. After preliminary testing, trying to find circles in the image proved to have too much delay in the system and the pinballs would move too fast to be detected at high speeds. Secondly, when a pinball is moving at high speeds, it appears on the camera with a large amount of blur surrounding it. This could greatly throw off the detection of circles as the shape no longer appears circular. The major disadvantage of this was the frequent mislabeling of other objects as pinballs. Many times when lights would turn on or other things would move on the playfield they would be categorized as a ball. This, however, proved not to be a major issue as they would not typically appear in the flipping area. If we were to include velocity tracking over time of a specific pinball, this would have been a greater issue.
Locating the flippers in track_metal.cpp is done in 6 steps:
- Create a HSV threshold that the flippers must fall between. Because they are bright yellow, it can be a relatively narrow hue threshold.
- Enlarge anomalies found in the binary comparison using cv::dilate().
- Retrieve a list of contours from this binary comparison using cv::findContours().
- Find the centroid of each contour in the list.
- Sort the centroids by their distance to the center of the frame.
- Return the two most central contours as long as their areas are between a certain threshold. They will be the two flippers.
This method for finding the flippers was extremely effective and accurate. The only issue we had when implementing this on the real system was the introduction of the yellow acrylic. This acrylic fell into almost the same HSV color space that the flippers did. The above-mentioned steps, however, take the center two contours, which the flippers always were.
Lastly, the track_metal.cpp node tells the system to flip the flippers when the detected pinball falls into a desired “flip zone” that is attached to the flippers. The “flip zone” is created by initializing a circle on the center of rotation of each flipper, turning it into a N-gon with a custom function, then removing most of the points of that N-gon to create a wedge. This method allowed the user to tune the center of the circle location, the radius of the circle, and which points to remove from the circle. This tuning allowed certain shots on the playfield to be hit more frequently or less frequently and favoring different types of playstyles.
The run_low_level.py node is responsible for, in software, keeping track of the status of all playfield components. This includes physical components like the switches and lights, but also digital components like the score, mode, and bonus multipliers. Every switch on the playfield has a ROS callback that triggers when the switch is hit by a pinball. These switch callbacks all mostly do the same thing such as updating the score and turning on a light. Some of the callbacks, like the switch that activates multiball, are slightly more complicated.
In addition to keeping track of all the playfield components in near-real-time, the run_low_level.py node is in charge of telling the Arduino to turn lights and solenoids on and off. To do this effectively, we make use of a python scheduling package called apscheduler. This package allows the developer to provide a ‘datetime’ formatted time to schedule a command or function to run. It does not, however, allow for a developer to say they want a command to be run ‘x seconds in the future’. We wrapped this functionality into run_low_level.py to allow for lights to be turned on and told to turn off in ‘x seconds’.
run_low_level.py also provides some functionality to keep track of a list of high scores for manual play. After each game, users can enter their name and it will check their score against a list internally. If they make it into the top ten players, their name is slotted into the list. To store and retrieve this list, the python pickle package was used for writing a serialized list to a file. Additionally, the list is initialized to a set of default scores if the user has not created the list before. The last node, GUI.py, is extremely simple compared to the others listed. The only thing that GUI.py does is display on the screen the current score, bonus, and play mode. All three of these enter GUI.py by a ROS topic that updates the GUI object. The GUI was built using the python Tkinter package. This package is incredibly simple to use and provides a sleek look.
Step 12: Change Pin Numbers, Upload Code to Arduino, & Update USB Camera
So, your machine is obviously going to be a little bit different than ours, so your pin numbers will not be the same. in order for this to work, you will need to update the pin numbers in two places: Arduino/Input_Ouptut/Input_Ouptut.ino and src/Classes/playfield.py.
You will also need to adjust the scripts to take out any reference to these switches and LEDs. The nice thing about this though is that they are super easy and basically all the same if you need to add more. In playfield.py make I have everything organized (roughly) by three rows, top, middle, and bottom. These rows helped me separate the items, that way I didn't have a single list of lights and single list of switches with a million items in each.
The playfield.py keeps track of how many there are and you will need to manually set the pin numbers for each, but it's not too hard to keep track of if you keep it organized.
Once all the pin numbers have been updated to your specifications, you will need to upload the code to the arduino. For this to work, you have to have installed the rosserial_arduino mentioned in the previous step and setup your Arduino IDE correctly with the ROS bindings.
The last thing we need to do is update the camera name in our code. The USB camera we used will most likely have a different name to the one that you use. It's also super easy to do, just find the line in src/Track/track_metal.cpp:
"std::string camera_string = "/dev/v4l/by-id/usb-046d_Logitech_Webcam_C930e_6D6BFE5E-video-index0";"
Update the string to the name of your camera, it will probably be something like "/dev/v4l/by-id/"
After you do that, you will need to re-compile for it to work, just cd back into the 'build' directory we created earlier and re-build the system by typing in 'make'.
After that, we're ready to go!
Step 13: Run the Machine!
If everything is hooked up correctly, and you are ready to have the time of your life playing pinball, all you have to do is cd into the 'launch' directory and type 'roslaunch automatic_pinball_c.launch'
This will start up all the associated nodes with the pinball machine including the GUI node and the node that tracks the position of the ball.
Additionally, you can use the 'roslaunch manual_pinball.launch' to not run any of the autonomy portion, but just the manual mode. This includes lights, bumpers, slingshots, etc.
Second Prize in the
Arduino Contest 2020