Introduction: Interactive LED Beer Pong Table

UPDATE: The RaveTable is the successor to this Interactive LED Beer Pong Table and is available for purchase at www.chexal.com/ravetable.

Create your own Interactive LED Beer Pong Table!

This instructable will guide you through all of the steps to in order to create a one-of-a-kind beer pong table complete with cup detecting RGB pods, automatic ball washers, a 32x12 ping pong ball LED grid, side LED rings for spectators and an RF interface to communicate wirelessly with a PC! It will teach you everything from theory of operation to modifying the table to suit your needs. First, I will take you through the modification and wiring of the table before we dive into the software side of things.

The toughest part about this project is just getting it started. There is a lot of prepping and labour to do before you can get anything real exciting working. However, if you can stick it out until you get the 32x12 LED grid in place, you will do just fine. Once you get up to that point, you can really start to see the potential for the table and it makes working on it a lot more enjoyable. I worked on this table on and off over the course of one year. If I were to build another one and had a set schedule of 8-hours per day to work on it, I could easily finish it within one week. The majority of the time that I spent on this project went into prototyping, development and writing the software rather than actually assembling the project.

Now come and take a tour with me through this Instructable and let us find out if you are up for the challenge!


Final Update

I have published a new Instructable that details the operation of the X5 kit and its capabilities. Although I modified the size of the table in the new Instructable, the X5 kit can handle everything that this version can do as well as more. In otherwords, customize your table however you like, whether you build off of this tables layout, the new tables layout or you completely design your own table layout. Thank you to everyone who has been patient with me! There was a lot of time needed to completely re-design the electronics and go through a few revisions before I had a solid product. Cheers!


Step 1: Intro: Gallery

Before we get started we are going to need a little bit of motivation, take a look at these pictures and realize what you will be making. This project is not for the faint of heart, not because it's difficult, but because it is very tedious and time consuming. Any time that you get frustrated while making this project, take a deep breath, come back to this step, take a look at the pictures and remember what you are working towards!

The pictures and videos that I took don't even do the table justice either, it really is something else to see up close and personal. The reward outweighs the risk and this one-of-a-kind project is definitely worth building! But we've got a long road ahead of us so lets get to it!

Step 2: Theory: What Is Beer Pong?

Beer pong is a drinking game in which players throw a ping pong ball across a table and try to hit one of the ten 1/3 filled beer cups on the other side. Once a cup has been hit, the beer is drank from it and it is removed from the table. The first team to clear the opposing teams cups wins the game. Although it is called beer pong, the game can be played with any beverage in the cups such as beer, juice or water. In fact, the World Series of Beer Pong (WSOBP) has switched to only using water in its tournaments so that they abide with all laws and regulations for playing beer pong in bars and public places.

There are many variations to the game so it is a good idea to go over the rules with each team so that each player knows what rules apply before the game begins. If you are unfamiliar with the game of Beer Pong, check out the link below.

Click here to read up on the official tournament rules and typical house party rules.

Step 3: Theory: Project Overview

Now before we start building this project we should get a little bit of theory under our belts! The playing surface on this beer pong table is a 2'x8' Lexan sheet which is considered regulation size. There is 6 inches of extra table on each side of the Lexan sheet which is only used for spectators and their drinks, giving the table a total size of 3'x8'.

The table contains 384 individually controlled LEDs with half of a ping pong ball over top of each LED, 20 RGB Pods that are able to detect whether a cup is over top of the pod and 2 ball washers that will pump a ping pong ball from one side of the table to the other with water, clean it, then blow it back up so that the player can grab it. On each side of the playing surface, we have a 6 inch wooden rail with 4 LED rings per rail and two LED rings for each ball washer.

I have attached a PDF of my AutoCAD drawing seeing as the jpg file isn't very clear. You will want to print this sheet out and use it for reference when we are modifying the table. I have also attached the AutoCAD 2012 file if you need to modify the table to suit your needs. You may notice that the creation date on that file is August 16, 2011. I actually designed this table and demoed the RGB pods, ball washers and LED grid quite a few years ago. I had just never got around to building the table and integrating all of the features together up until one year ago.

32x12 LED Grid
In the center of the table we can create any animation that will fit into a 32x12 pixel grid. We are able to display scrolling text across it, watch a pong animation, display a sine wave, display the score of the game, etc. There is a huge amount of possibilities! Since we are able to detect when a cup is removed from the table we can make specific animations that trigger exactly when that happens. We'll get more in-depth with that later.

20x RGB Pods w/ Infrared Sensors
At each end of the table you will see 10 pods. The 16oz cups that are used for beer pong are placed over top of these pods. Each pod contains 4x RGB LEDs and 1x infrared sensor and we are able to light up the pods with any color that we would like. The infrared sensor will detect whether or not a cup is over top of the pod, so if a cup is removed we can change the color of the pod, begin an animation on the LED grid, run an animation on the RGB pods, etc.

Ball Washers
There is a ball washer on each side of the table. The purpose of the ball washers are to...wash the ping pong balls (who would have thought, right!?). The 4x cyan colored holes on the AutoCAD drawing are used to mark the ball washer placement. A player drops a ball into the ball washer hole that is to their left, an infrared sensor will detect the ball and activate the water pump, the water will then push the ball down the pipe while cleaning it, all of the water will be strained out before the end of the pipe and then a fan will turn on and blow the ping pong ball up and out of the ball washer hole to the right of the player. Once the ball is grabbed by the player, the infrared sensor on the right side of the ball washer will detect that the ball is gone and shut off. Each ball washer hole also has a LED ring around it that can be used for animations.

LED Rings
There are a total of 12 LED rings on the beer pong table. The outer railing of the table contains 8 LED rings and the ball washers use the other 4. Much like the 32x12 LED grid, the LED rings are just used for animations. They can be set to go in accordance with music (VU Meter feature must be turned on), fade in and out, flash rapidly or any other cool animation that you can think of. Beer cups can be set inside of the LED rings on the railing which provides a cool effect on the upper lip of the cup.

VU Meter
I have implemented a microphone and an audio amplifier which feeds into the ADC on the microcontroller so that we are able to detect different intensities of sound with the microcontroller. This really adds to the effect of the table and looks really cool as you can turn on different features of the table to different intensities of sound, display a VU Meter on the LED grid and much much more.

RF Module
I have added support for an nRF24L01+ RF module on this beer pong table so that we can control the beer pong table from a laptop. We can send text from the PC to scroll across the LED grid, we can individually select the color of each RGB pod, we are able to set the grid brightness, etc. Seeing as I am still learning the USB protocol, I do not feel comfortable posting my PC software source code yet as it is still really messy and may contain a few bugs. When I get it cleaned up I will be sure to post it! Not a big deal though, I have posted the RF transmitter source code, the PC executable and the source code for the beer pong table in step #82 so that we can still test it out.

The cost to make this beer pong table is around the $400 - $500 mark. The major costs are the 2'x8' 1/8" Lexan sheet which was $83, the table itself which was $30 (I got a good deal on a used one) and the 500' feet of CAT5 (you won't use all of the CAT5, better to have too much than not enough though) which you should be able to find for $30 - $60. Seeing as I worked on this project over the course of a year, I purchased everything that I needed at different times throughout the year. I never had to fork over $400 at once, just $50 here and $50 there, so it didn't seem as expensive at the time.

This was just a brief overview of the table. We will go much more in-depth as we get to each feature that is available to us. Take a deep breath, in the next few steps we are going to prepare for construction!

Step 4: Preparation: Skills and Software Required

If you look at this project as a whole it may seem very intimidating. The trick is to break each part down into 'mini' projects and integrate them together. You don't build the whole project, wire everything together and then turn it on hoping that it will all work because chances are that it won't. Instead, we take baby steps and separate the project into smaller sub-projects, testing the workings of each sub-project before moving on to the next one. By doing it this way you can work out any issues one at a time as you progress.

The following knowledge and skills will help aid you through this project.
  • An understanding of digital electronics (multiplexing, data transfer, etc)
  • An understanding of C Programming (The whole project is written in C with MPLAB's C30 Compiler)
  • An understanding of binary and hexadecimal numbering systems
  • How to solder (This includes SMD components)
  • How to crimp ends and create connectors (RJ45, Molex, etc)
  • How to use test instruments such as a multimeter
  • How to operate power tools (table saw, drill, jigsaw, etc)
  • How to create PCBs or set up circuits on veroboard
If you are not good at soldering SMD components you may want to get somebody who is good at it to help you. The PIC24HJ128GP506A has 64-pins that are 0.22mm wide and only have a 0.28mm space between pins.

Software
All of the software for the beer pong table has been written in C. You will need to download the MPLAB IDE and install it, as well as the C30 compiler to go with it. If you plan on using the RF feature of this table, you will also need the C18 compiler as the RF Master Board uses a PIC18F4550 to control USB and wireless operation.

Step 5: Preparation: Electronic Components

There are 24 PCBs that are required to create this project. Now, 20 of those PCBs are actually single RGB Pods that are placed under the cups (10 on each side). There is then 2 RGB Pod controllers (1x for each side of the table), the 32x12 LED grid controller PCB and the Master PCB which contains the microcontroller. I have created a component list for each category and added up the total amount of components that are needed to assemble the required amount of PCBs in that category.

The majority of these components can be found on eBay but there are a select few which may prove more difficult to find. I have attached a Bill Of Materials to this step which has each component, the required quantity, the price and the vendor where the component(s) can be purchased. The total bill for all of the components (including both power supplies and blue LEDs) listed below comes to $154.66.

Power
1x 12V 5A Power Supply (I used a 12V 10A power supply, only a 5A is needed)
1x 5V 5A Power Supply (I used a 5V 10A power supply, only a 5A is needed)
1x 120V Power Bar
2x 24" 120V Power Cords

32x12 LED Grid Controller (1x PCB)

  • 2x HT1632C LED Drivers
  • 1x 2.1mm Power Jack
  • 2x Quad RJ45 PCB Mounted Jacks
  • 3x Single RJ45 PCB Mounted Jacks
  • 16x Quad 150Ω 0603 SMD Resistor Networks
  • 500x 5mm Blue LEDs (these are not on the PCB, they are mounted on the table)


RGB Pods (20x PCBs)

  • 80x RGB PLCC-6 SMD LEDs
  • 60x MMBT2907A SOT23 PNP Transistors
  • 60x Quad 150Ω 0603 SMD Resistor Networks
  • 20x TCRT5000 Reflective Optical Sensors
  • 20x 270Ω 0805 SMD Resistors
  • 20x 39kΩ 0805 SMD Resistors
  • 20x 8-Pin 0.1" Polarized Male Connectors
  • 20x 8-Pin 0.1" Polarized Female Connectors
  • 160x Crimp Pins for Connectors


RGB Pod Controller (2x PCBs)

  • 4x TLC5940 16-Bit PWM Drivers
  • 4x 74HC4051 8-Channel Input Multiplexers
  • 4x 9-pin 0.1" Headers
  • 4x Quad RJ45 PCB Mounted Jacks
  • 4x Dual RJ45 PCB Mounted Jacks
  • 2x Single RJ45 PCB Mounted Jacks
  • 2x 2-Pin 0.1" Headers
  • 4x 470Ω 0805 SMD Resistors
  • 4x 2.4kΩ 0805 SMD Resistors
  • 4x 39kΩ 0805 SMD Resistors
  • 2x 0.1µF Ceramic Capacitors
  • 2x 0.01µF Ceramic Capacitors
  • 2x 10µF Electrolytic Capacitors
  • 2x 220µF Electrolytic Capacitors


Master Controller (1x PCB)

  • 1x PIC24HJ128GP506A-TQFP 10mmx10mm 16-Bit Microcontroller
  • 1x TLC5940 16-Bit PWM Driver
  • 1x LM386 PDIP Audio Amplifier
  • 2x ULN2803ADWR 8-Channel Darlington Array Driver
  • 2x MMBT2222A SOT23 NPN Transistors
  • 2x 74HC540 8-Bit Inverting Line Drivers
  • 1x 74LVC2G125DCUR Line Driver
  • 1x LM2576-3.3 Switching Regulator 3.3V 3A
  • 1x Quad RJ45 PCB Mounted Jack
  • 1x Single RJ45 PCB Mounted Jack
  • 2x 1000µF Electrolytic Capacitors
  • 1x 220µF Electrolytic Capacitor
  • 2x 10µF Electrolytic Capacitors
  • 1x 1.0µF Electrolytic Capacitor
  • 1x 0.1µF SMD 0805 Capacitor
  • 4x 0.01µF SMD 0805 Capacitors
  • 2x 27pf SMD 0805 Capacitors
  • 3x 1N4148 Diodes
  • 1x 1N5822 Schottky Diode
  • 1x 100µH Power Inductor (12mmx12mm)
  • 4x 10kΩ 0805 SMD Resistors
  • 3x 18kΩ 0805 SMD Resistors
  • 1x 2.4kΩ 0805 SMD Resistor
  • 1x 47kΩ 0805 SMD Resistor
  • 1x 100kΩ Trimmer Potentiometer
  • 1x 10kΩ Trimmer Potentiometer
  • 1x 2kΩ Trimmer Potentiometer
  • 1x 20MHz Crystal
  • 1x 2.1mm Power Jack
  • 16x 2-Pin 0.1" Male Polarized Connectors
  • 3x 2-Pin 0.1" Male Headers
  • 2x 3-Pin 0.1" Male Polarized Connectors
  • 1x Electret Microphone
  • 2x 9-Pin 10kΩ 0.1" SIP Resistor Networks
  • 1x SPDT PCB Mounted Switch
  • 1x 8-Pin Dual Row Connector
  • 2x 5-Pin 0.1" Male Headers


The passive components and connectors I usually purchase from DIPMicro or eBay. Many of the components/connectors can be changed according to what the user likes, the footprints might just not match the PCB layout. If you are breadboarding or point to point soldering everything together then this wouldn't be a problem.

For prototyping with the PIC24HJ128GP506A you can use a breakout board from eBay.

Step 6: Preparation: Required Materials

Now we need to acquire the materials that go along with the electronics. The most obvious part that we need is a 3'x8' table. One could go ahead and make the table themselves but I opted to purchase a table just to save time. I found my table in the classified ads for $30, I couldn't even build one from scratch for that cheap so I chose to take it and use it. Here is a list of the materials needed to complete this project. Where I'm from, stock measurements on materials are still in imperial units so I do switch back and forth between metric and imperial units in this instructable. When I cut the material for the table I use imperial units, when I am modifying the table and drilling holes I use metric (as you will see on my CAD drawings).

I bought all of my plywood/OSB from Home Depot and I had them cut all of my 6¼"x96" pieces for $1 per cut ($4 total). I do have a small table saw to do the cuts myself but it was still easier to get them to cut it down for me. As for the lexan sheet, I just found a local plastics company in town, stopped in and talked to a representative and got a 2'x8' Lexan sheet for $83.

Hardware

  • 1x 36"x96"x3/4" Table about 27" high (Height can vary)
  • 1x 24"x48"x1/8" Lexan Sheet
  • 2x 6¼"x96"x7/16" pieces of plywood or OSB
  • 2x 6¼"x96"x½" pieces of plywood
  • 2x 1¼"x23½"x7/16" pieces of plywood or OSB
  • 2x 1¼"x23½"x3/8" pieces of plywood
  • 1x 10"x20"x½" piece of plywood (Can be rough cut, this is what we attach the PSUs and PCBs to)
  • 2x 21½" pieces of 1½" ABS pipe
  • 2x 11/16" long pieces of 3" ABS pipe
  • 2x 1½" ABS T-Connectors
  • 2x 1½" 90º ABS Elbows
  • 1x 20mm length of 1½" ABS pipe
  • 1x 40mm length of 1½" ABS pipe
  • 2x 24" Lengths of 5/16" tubing
  • 20x 2"x½"x½" Pieces of plywood (These are spacers used under the RGB pods)
  • Steel Strapping
  • Screws (1.5" and 3/4" Lengths are used)


Miscellaneous Items

I will not provide in-depth instructions to build the table itself but I have attached a few Sketchup drawings that show all the parts needed and measurements to build your own if you can't find one to purchase (it is a pretty common table.) If you need to make your own table, it can be made from one 48"x96"x3/4" sheet of plywood.

Step 7: Preparation: Tools Needed

We're almost ready to start building! The last thing that we need to do is gather up some tools. If you don't feel comfortable using a table saw, talk to a local carpenter and have them cut the wooden rail pieces for you. They are really basic cuts so I would imagine that it would be a relatively cheap price. Below is a list of tools that we will use to construct this project.

Tools
  • Drill
  • Jigsaw
  • Drill Press
  • Table Saw
  • Hole Saw Kit
  • Tin Snips
  • Soldering Iron w/ Solder
  • #2 Robertson Screwdriver
  • Wire Cutters
  • Wire Strippers
  • Molex Crimpers
  • RJ45 Crimpers
  • Glue Gun
  • Hacksaw
  • Rotary Tool (Dremel) with Circle Cutter Guide
  • Scroll Saw (Optional; Used for cutting PCBs)

Those are the tools that I had used to complete this project. A miter saw would help a great deal too for smaller wood cuts and cutting the ABS pipe, however, I did not have one.

Step 8: Construction: Drawing the LED Grid

The 32x12 grid consists of 384 LEDs that are each spaced out from each other by a 50mm gap. The first thing that we need to do is draw out the grid on the table. I would recommend using a black washable marker as we will want to remove the black lines later in the project. I ended up using a permanent sharpie to draw my grid but I had to really scrub the lines with a napkin and acetone to get them removed. I wouldn't recommend doing it that way.

Now grab the AutoCAD drawing that you printed out from step #3. First, we will start by marking 32 rows that span across the width of the table. Choose one end of your table to be the master and the other to be the secondary. The beer pong table is symmetrical so what we do to one half of the table we will do the exact same to the other half, but it is still important to label each end of the table so that we have a reference for when we start adding the electronics.

From the Master Side of the table, measure 444.2mm into the table and make a tick with your marker. This will be ROW0. From that tick measure up another 50mm and make another tick. This will be ROW1. Measure another 50mm and make a third tick which will be for ROW2. Keep repeating this until you get to ROW31. Once you have your measurements on the left and right side of the table, take a level and draw a straight line across the table for each row.

Once you have marked all of the lines for the rows, we will start measuring out the placement of the columns. At the Master End of the table measure in 182.2mm from the left side (it doesn't actually matter which side you reference from as it is symmetrical) of the table and make a tick mark with your marker. This will be COL0. Continue over 50mm and make a mark for COL1. For each column keep incrementing by 50mm until you get to COL11. Now repeat this procedure at the secondary end of the table and if you only have a 4' level you will have to do this in the middle of the table as well. Once you have the columns measured at the master end, secondary end and middle of the table, take your 4' level and start connecting the ticks to complete the grid.

Step 9: Construction: Drilling Out the Grid

Now that we have the LED grid drawn out, we can begin drilling the holes for our LEDs. A hole is drilled at each intersection of the grid. The first holes that we drill are 11/64" (4.4mm) in diameter and will be drilled all of the way through the table. The leads on the LEDs can fit through this hole but the 5mm LED itself can't. Once we have drilled out all 384 holes we will get a 15/64" (5.8mm) drill bit and drill 1/4" deep into all 384 pre-drilled holes so that we can place a LED in each hole. On the bottom of the table I took a 3/8" drill bit and drilled about 1/8" deep in each hole which allowed better access to the LEDs leads. This is optional.

The LEDs that I purchased had leads that were shorter than normal (the seller on eBay failed to specify this or I wouldn't have bought them). If I didn't countersink my LEDs, the leads wouldn't have been accessible from the underside of the table and I wouldn't have been able to solder them together. You don't need to waste your time countersinking on the bottom if your LEDs have leads that are long enough to be accessed from underneath the table without countersinking.

Step 10: Construction: Installing the LEDs

It's time to break out the LEDs and a hot glue gun! After we get the LEDs installed, we will be wiring them up in the next step. It is very beneficial for us to install all of the LEDs in the same orientation as one another to make the next step easier! In other words, don't just randomly place the LEDs in their respective holes. Have all of the LEDs cathodes facing the same edge of the table and all of the LEDs anodes facing the other side of the table (refer to photos to verify). It is a good idea to test each LED before installing it so that you are 100% sure that none are faulty.

That being said, all that we have to do now is put a small drop of hot glue right underneath the LED casing and push the LED into place. Pretty easy, right? Good. Now we just have to do it 383 more times. ;)

There are 16 "special" LEDs on the table that are located right above the tables legs (8 on each side of the table). These LEDs have to have a longer length of wire soldered to them before we push them through to the bottom of the board otherwise their leads won't be accessible.

Step 11: Construction: Wiring Up the LED Grid

The HT1632C LED Driver uses a method called 'multiplexing' to control its LEDs. Logically thinking, if you wanted to control 384 LEDs you would need 384 outputs. There aren't many microcontrollers that have that many outputs, so most users opt for the multiplexing approach. By connecting up the LEDs in a grid-like format and scanning through each column at speeds faster than the human eye can perceive, we can make it appear as though each LED is on at the same time, when in fact, we are actually only controlling one column of LEDs at a time.

In order to wire up all 12 columns, we just connect the cathodes of each LED in their said column together. To wire up all 32 rows, we connect up the anodes of each LED in their said row together. There is one slight catch though (refer to the schematic; Photo #1). If the HT1632C is configured to control 32 rows, then it can only control 8 columns per chip. This is a problem because we have 12 columns on our grid. We need to cascade 2x HT1632C LED drivers so that we are able to control columns 8, 9, 10 and 11. When we wire up the anodes in our rows, we only connect up the first 8 LEDs anodes together then we wire up the next 4 LEDs anodes together. It is very important to keep them separated as they will be driven by two separate chips. I connected all of the anodes together in their rows first, then took my snips and separated COL0 - COL7 and COL8 - COL11 afterwards. For the cathode connections I just used bare copper wire and for the anode connections I used solid 26 AWG wire that I got out of phone cable (any wire will do, CAT5 strands would work great). At every solder point you have to strip back a bit of the insulation on the wire to be able to solder to the anode of the LED.

CAT5 was my wire of choice for this project as it is very cheap and contains 8 wires within one PVC jacket. Seeing that we have a 32x8 grid to connect up to HT1632C #1, we have 32 row connections and 8 column connections that need to be wired into this driver chip. We will use 4x separate CAT5 lines for the rows (4 lines * 8 wires/line = 32 wires) and 1x CAT5 line for COL0 - COL7 (8 wires). **We do the exact same wiring for HT1632C #2, except it only uses 4 out of the 8 wires for COL8 - COL11. It is crucial to keep all of the wiring organized and in a pattern. It doesn't matter what pattern of wire colors that you use as long as you are consistent the whole way through. We will be crimping RJ45 ends on the other side of these CAT5 lines so be sure to record what pattern you use. The wire pattern that I used (TIA 568A Standard) is as follows:

Wire# - Color
1 - Green/White
2 - Green
3 - Orange/White
4 - Blue
5 - Blue/White
6 - Orange
7 - Brown/White
8 - Brown

Keep in mind that we will be routing all of the cables to the PCBs which will be installed underneath the center of the table. I made sure that all of my CAT5 lines were 10' long so that I had plenty of cable to work with. It's better to have too much cable than too little, and being such a large project it is easy to hide the cables underneath at the end of the project.

**By using 2x HT1632C driver chips we could actually control a 32x16 LED grid. We only need a 32x12 LED grid so we won't use the other 4 available columns on the 2nd chip.

Step 12: Construction: Crimping on the RJ45 Connectors

To finish up the construction of the LED grid we just have to crimp some connectors onto the ends of our CAT5 lines and then we will be ready to build the LED grid controller PCB!

If you followed my wiring pattern, the connections for HT1632C #1 are as follows:

Line #1
ROW0 - Green/White
ROW1 - Green
ROW2 - Orange/White
ROW3 - Blue
ROW4 - Blue/White
ROW5 - Orange
ROW6 - Brown/White
ROW7 - Brown

Line #2
ROW8   - Green/White
ROW9   - Green
ROW10 - Orange/White
ROW11 - Blue
ROW12 - Blue/White
ROW13 - Orange
ROW14 - Brown/White
ROW15 - Brown

Line #3
ROW16 - Green/White
ROW17 - Green
ROW18 - Orange/White
ROW19 - Blue
ROW20 - Blue/White
ROW21 - Orange
ROW22 - Brown/White
ROW23 - Brown

Line #4
ROW24 - Green/White
ROW25 - Green
ROW26 - Orange/White
ROW27 - Blue
ROW28 - Blue/White
ROW29 - Orange
ROW30 - Brown/White
ROW31 - Brown

Line #5
COL0 - Green/White
COL1 - Green
COL2 - Orange/White
COL3 - Blue
COL4 - Blue/White
COL5 - Orange
COL6 - Brown/White
COL7 - Brown

The connections for HT1632C #2 are as follows:

Line #6
ROW0 - Green/White
ROW1 - Green
ROW2 - Orange/White
ROW3 - Blue
ROW4 - Blue/White
ROW5 - Orange
ROW6 - Brown/White
ROW7 - Brown

Line #7
ROW8   - Green/White
ROW9   - Green
ROW10 - Orange/White
ROW11 - Blue
ROW12 - Blue/White
ROW13 - Orange
ROW14 - Brown/White
ROW15 - Brown

Line #8
ROW16 - Green/White
ROW17 - Green
ROW18 - Orange/White
ROW19 - Blue
ROW20 - Blue/White
ROW21 - Orange
ROW22 - Brown/White
ROW23 - Brown

Line #9
ROW24 - Green/White
ROW25 - Green
ROW26 - Orange/White
ROW27 - Blue
ROW28 - Blue/White
ROW29 - Orange
ROW30 - Brown/White
ROW31 - Brown

Line #10
COL8   - Green/White
COL9   - Green
COL10 - Orange/White
COL11 - Blue

It doesn't matter if you crimp the 4x remaining wires on Line #10 in the RJ45 connector, they just won't serve any purpose.

Well good work! We just finished the construction side of the LED grid. Next we will build the driver, test the LEDs and then install the half ping pong ball covers over the LEDs. After all of that work we can finally see some exciting progress!

Step 13: Construction: Building the LED Grid Controller

I have uploaded a pdf file in this step that contains the mirrored image of the top of the PCB and the bottom image of the PCB. They are already set to scale in the PDF so that you only need to print them at a 1:1 scale. I used the toner transfer method to create my PCBs. If you are unfamiliar with this method of PCB fabrication, just take your pick of the many instructables that explain how it works. Once you're up to speed on how to make PCBs with the toner transfer method (or any other method that will work), come back here, download the PDF and create the PCB.

Once we have the PCB created and assembled, we will have to connect up all of the lines from the grid. The connections are as follows:

CON1
PORT1 -> Line #1
PORT2 -> Line #2
PORT3 -> Line #3
PORT4 -> Line #4

CON2
PORT1 -> Line #6
PORT2 -> Line #7
PORT3 -> Line #8
PORT4 -> Line #9

CON3
PORT1 -> Line #5

CON4
PORT1 -> Line #10

If you do not know how to make PCBs you will have a hard time finding a breakout board for the HT1632C chips as they are 14mmx14mm with 1.0mm spacing between pins. On this forum, user mmcp42 has created a breakout board and uploaded the eagle files which allows others to take the files and send them to a PCB fabhouse where they can be professionally made. This is just an alternative solution if you are veroboarding the project.

I have done a PCB test run with one company and ordered fifty HT1632C Breakout PCBs. Private message me if you would like to speak for one or more ($2.50 per HT1632C breakout PCB, silkscreen was mirrored at the time of order, so don't use the silkscreen as a pin reference) or purchase them on eBay here. I have also added the HT1632C breakout board in PDF format (1:1 Scale) to this step if you would like to make the breakout board yourself.

The gerber files are available for download if you want to get your own PCBs professionally made.

Step 14: Construction: Building the Master PCB

In order to test the LED grid that we just constructed, we will need to create the Master PCB and hook the LED Grid Controller up to it. The first time I printed this PCB layout onto glossy paper, I forgot to uncheck "Scale to page" in Adobe Reader. I didn't notice this mistake until I had fully etched the board and was trying to line up the PIC24HJ128506A on its respective pads. Needless to say, when I made the next PCB I made sure that the printout was only scaled 1:1 and I lined up the PIC on the new printout before actually etching the board. This go around it turned out exactly how it was supposed to.

The PCB that I am using on my beer pong table is v1.1. The main difference between v1.1 and v2.0 is that there is no longer a 5V regulator on the PCB. When I was prototyping the table and had everything connected to a breadboard I was having a little bit of noise issues when I was sending SPI data to the RGB pod controllers. I had actually expected that would happen seeing as the signal is running through 15" - 25" of copper track and wire (that's a long ways for a high-speed SPI signal to travel) so I simply installed a buffer. This took care of the problem and everything worked fine until I was done prototyping. When I made the PCB I encountered the same noise errors on the SPI line from when I was prototyping only this time I did have a buffer installed. I scanned the PCB, added decoupling caps, shortened cables, etc. and could not get it to operate properly. I eventually figured out that it was the 5V power supply on the PCB that was creating the noise, so I bought a 5V 10A power supply unit, modified my PCB, removed the 5V power supply from it and connected up the new 5V 10A external power supply. This took care of the problem and everything began working exactly as it was supposed to again.

On revision 3.0 I will create another 5V power supply on the PCB without the added noise so that I only have to have one external power supply unit (+12V) instead of two. This works just fine for now though. Remember to take your time assembling this PCB and verify that you haven't created any solder bridges as there are a lot of small SMD pads on this board. Once you get the board assembled, we will be testing it out in the next step.

The gerber files are available for download if you want to get your own PCBs professionally made.

Step 15: Construction: Connecting the LED Grid to the Master PCB

Before we connect the Master PCB and the LED Grid controller together, we are going to do a programming test on the Master PCB. Connect up a 24" 120V cord to each power supply unit and plug them into the power bar (Don't plug the power bar in yet though). On the power cable that goes from the 12V PSU to the Master PCB we will have to install a 2.1mm plug on one end of the cable. For the 5V PSU we need to strip 1/4" of insulation off of each end of the cable and attach it to the screw terminals on the PSU and the PCB. Once you have the power to the Master PCB all connected up, plug the power bar in and turn it on.

If all seems well, connect your programmer of choice (I use a PICKIT3) onto the ICSP header and prepare to program the device. The code that I used to test the LED grid is in a downloadable zip file at the bottom of this step. You will need MPLAB and the C30 compiler to be able to build and program the code. Once you program the code into the microcontroller, turn off the power to the Master PCB and connect up the LED Grid Controller to it. We will need one short CAT5 cable (straight through cable, not a crossover cable) to connect up the data and control lines of the HT1632Cs to the Master PCB. We will also need to make a 3-Pin female connector (We don't use the 3rd pin, I just didn't have a 2-pin connector) to a 2.1mm plug in order for us to connect 5V power to the LED Grid Controller. Refer to photos for help.

At the end of the project we will tidy up the wiring and hide it all under the table, until then you can leave it all loose in case you need to make any changes. Once everything is connected, turn on the power again and you should see the LED grid come to life! The LED grid will cycle through ten different LED grid animations. Do not worry about how the code works yet, lets just get done building the table first. The documentation for the code starts at step #43.

In this video you will notice that some of the LEDs do not light up at the end of the animation, this was intentional (I was drawing circles and expanding them, that's why some LEDs didn't get lit up). All of the LEDs are tested and working.

Step 16: Construction: Prepping the Ping Pong Ball LED Covers

It's time to make the ping pong ball LED covers! By the time you get to the 384th LED cover, you'll be really good at this. We'll need a utility knife, sharp scissors and 200 ping pong balls for this step (technically we only need 192 ping pong balls, it's a good idea to have extra in case we mess up).

Cut into the ping pong ball carefully with the utility knife and slowly cut across the middle of the ball until it is split into two halves. Don't worry if one half is taller than the other because we still have to take our scissors and trim off the center band that was used to link the two halves together. Once we trim off the center band from the balls they will be closer in height to each other. As long as each half is in between 5/8" and 3/4" tall, it will be fine (it's pretty easy to do this consistently). If there is a logo on any of the halves, sand it off with some fine sandpaper. Put the two halves into a pile once they are complete, grab another ball and repeat the process...191 more times.

Step 17: Construction: Installing the LED Covers

Finally, we are on the last step for the LED grid. I just eye-balled the covers when I glued them onto the table but it wouldn't be a bad idea to create a jig of some sort to keep everything perfectly straight if you are having troubles with this step.

Make sure to scrub the black grid lines off of the table before you install the LED covers. You will see in my picture that there is still some marker around the LEDs, this is fine because the LED cover will hide that part of the grid anyways. I had used a permanent Sharpie marker and had to use a napkin and acetone to clean it off (it was a real pain). If you followed my advice earlier and used a washable marker, it will be much easier for you to remove the lines.

Grab one of the ping pong ball halves that you have cut apart and then take your glue gun and put two dabs of glue on the underside of the LED cover. You won't have much time until the glue hardens so you will want to get it centered above the LED as quick as possible. Once you have it centered, press it firmly onto the table until the glue dries (a few seconds) and then grab another cover and repeat the process on the next LED.

Here is what it will look like when it is done.



Step 18: Construction: Creating the RGB Pod Controllers

Now that we've completed the grid, we'll move on to the RGB pod controllers. There is a controller on each side of the table that controls the ten RGB pods and two ball washer sensors. The two controllers are identical to each other, so that makes it easier for us as we just have to make two copies of the PCB files.

There is a PDF file at the bottom of this step that contains the bottom and top parts of the PCB. Like the other PCBs, it has to be printed at a scale of 1:1 and the top of the board has already been mirrored for using the toner transfer method. Just print the files on glossy paper or PCB paper, laminate/iron them to the PCB, etch the boards and then drill them out.

The electronic components that are needed for the two RGB pod controllers have been outlined in step #5. Once you have the PCBs etched and assembled, connect a 9-pin 0.1" jumper across JMP_MAS and JMP_SLV (Check proper orientation). Now we're ready to make the twenty RGB pods!

The gerber files are available for download if you want to get your own PCBs professionally made.

Step 19: Construction: Making the RGB Pods

The RGB pods are the PCBs that go under each cup on the table. Each pod contains 4 RGB LEDs and an infrared transmitter/detector unit. With this setup, we are able to control the color of each separate RGB pod and we can detect whether there is a cup placed above the pod or not. When a cup is removed we can change the color of the pod, run an animation on the LED grid, activate the outside LED rings, etc.

If you have already created and assembled the previous PCBs in this project, these little RGB pods will be a walk in the park compared to those. As with the other boards, the component list is located in step #5, the PDF files for the toner transfer method are scaled at 1:1 with the top layer already mirrored and if you are using the PCB photos for the UV method the resolution for the photos is set at 300DPI.

Make the PCBs in batches of four, etch them, cut them out, round them off with tin snips, drill out the holes/vias and assemble them. Etch 5 batches so that we end up with the 20 RGB pods that we will need. We will then need to create 20 wiring harnesses to connect the pods up to their respective controllers. Each harness is an 8' long piece of CAT5, an 8-pin 0.1" polarized connector, 8 crimp pins for the connector and one RJ45 connector to interface the harness to the controller. Follow the same wiring pattern for each end of the CAT5 cable.

Pin - Wire Color
#1 - Green/White
#2 - Green
#3 - Orange/White
#4 - Blue
#5 - Blue/White
#6 - Orange
#7 - Brown/White
#8 - Brown

The gerber files are available for download if you want to get your own PCBs professionally made.

Step 20: Construction: Attaching the Pods to the Table

Grab the AutoCAD drawing of the table again as we are going to mark the pod locations on the table. We will start at the location of pod #10. Measure 314.33mm from the right edge of the table and up 73.02mm from the bottom edge of the table. Make a mark here as this is the location of pod #10. Now measure 95.25mm to the left from pod #10 and 73.02mm from the bottom of the table and make another mark, this is pod #9. Do this two more times until you get to pod #7. From pod #10 measure up 82.55mm and 47.63mm to the left to mark pod #6. From pod #6 measure over 95.25mm and 155.57mm from the bottom edge of the table (82.55mm + 73.02mm). Continue this pattern until you get pods #1 - #10 installed, then do it all over again on the Secondary side of the table for pods #11 - #20.

Don't draw a 1¼" radius around each pod location like I did. It doesn't serve any purpose and you will have to wash it off before installing the pods anyways. Insert a 3/8" drill bit into the drill and drill a hole that is 1" (25.4mm) to the left of each center mark. This is where we will feed the RGB pod CAT5 line to the underside of the table. Feed one RGB pod harness through each hole and plug the crimp pins into a connector on the top side of the table (wiring instructions in step #19).

Take all 20 of the 2"/1/2"x1/2" spacers that we have and drill a 3/16" (4.8mm) hole right in the center of each one. Attach each wiring harness to the connector under each RGB pod, feed a 1½" screw through each pod, add a spacer underneath, then place the pod in its location and screw it firmly to the table. Now we're ready to hook them up!

Step 21: Construction: Connecting the RGB Pod Controllers to the Master PCB

Label one of the RGB pod controllers as Master and the other as Secondary. Grab your 12" CAT5 patch cables and connect up port '5940 #1' on the Master PCB to 'TLC5940 Control' on the Master RGB Pod Controller. Then connect '4051 #1' on the Master PCB to '74HC4051 Control' on the Master RGB Pod Controller. On the Secondary RGB Pod Controller, 'TLC5940 Control' gets connected to '5940 #2' on the Master PCB and '74HC4051 Control' gets connected to '4051 #2' on the Master PCB. Refer to the photo for a visual diagram of these connections.

Next, we will hook up the IR sensor lines. JP3 on the Master Pod Controller PCB will connect to JP3 on the Master PCB, the orientation of the connector does not matter as the lines are actually connected up as one on the Master PCB. JP4 on the Master PCB must connect up to JP3 on the Secondary Pod Controller and once again, the orientation does not matter. Each connector is a 2-pin 0.1" female header. Now that we have the IR sensor lines hooked up we will be able to detect if cups are placed over the pods and if a ping pong ball has been placed in either of the ball washers.

The last thing that we need to do is connect up all of the RGB pods to their respective controller. RGB Pod #1 goes to #1 on the Master Pod Controller, pod #2 goes to #2 on the Master Pod Controller , all the way up to pod #20 on the Secondary Pod Controller. Once again, you can leave the wires hanging free while we are still in the testing stages. Be sure to connect up a 9-pin jumper across JMP_MAS and JMP_SLV on each RGB Pod Controller so that they will work properly!

Step 22: Construction: Testing the RGB Pods

Download the test program from the bottom of this step. Connect up your programmer to the master PCB via the ICSP header and start up the test program. The test program will cycle the RGB pods through six different animations. We will only be changing the colors of the RGB pods in this step, in the next step we will test the infrared sensors.

Seeing as we do not have the lexan sheet over top of the pods yet, I turned the beer cups upside down over top of the RGB pods to add a cool effect. The color is kind of washed out in the pictures, so make sure to check out the video below to see how vibrant it looks! The video cycles each pod through the 10 default colors.

Step 23: Construction: Testing the IR Sensors on the RGB Pods

Now that we know that the RGB pods are working, we will test out each infrared sensor on each pod. At the bottom of this step is a zip file that contains the source code, download it, open it, compile it and program it into the PIC on the master PCB. Seeing as we haven't installed the lexan sheet over top of the RGB pods yet, I had to use a small glass pane set on top the the RGB pods. This allowed me to place the beer cups over top of each pod and test it out. When you start up the beer pong table there can't be anything over top of any of the IR sensors. It takes a few milliseconds to calibrate the IR sensors right when it starts up, after that, cups can be placed over top of the pods. Once again, we will get to the software side of things in the other half of this Instructable.

Here is how it should look!

Step 24: Construction: Prepping the Rails for the Sides of the Table

Each rail consists of two pieces of plywood/OSB. The bottom part of the rail is 6¼"x96"x7/16" in size and the top part is 6¼"x96"x½" in size and fits directly on top of the bottom piece. The top part needs to be modified a bit as it has 4 LED ring holes and a ¼" wide by 1/8" deep notch down the length of the board (this is what holds the lexan sheet in place). Both the top and bottom pieces need to be modified to accommodate the ball washers too.

First, lets create the ¼"x96"x1/8" notch down the top rail. If you have a dado set, attach it to your table saw at ¼" width and set it at 1/8" deep. Grab the 6¼"x96"x½" pieces of plywood (the top of the rail) and run each one through the table saw for the full 96" length of the board to create the notch. I didn't have a dado set, so I set my saw blade to 1/8" deep and had to run each board through three times while increasing the width of the guide from the blade each time until I had a ¼" wide notch. This way is a bit more crude but it does work, just be careful of the clearance from the saw blade to the guide. Once the notch is cut out, take a sander and smooth the top of the rail.

Step 25: Construction: Cutting Out the LED Ring Cup Holders

Get the CAD drawing of the table again, measure out each center location for the 8 LED ring cup holders (4 per rail). Start at the master end of the rail and measure up 541.86mm. Now measure in from the outside edge 76.20mm and make a mark for LED Ring #1. The next ring is 451.56mm up from LED Ring #1, so measure 993.42mm up from the master end and in 76.2mm from the outside edge and place the mark for the 2nd LED ring. Keep going in 451.56mm increments for LED rings #3 and #4, then repeat the exact same process on the other rail.

Once we have the cup holders ready to be cut out, get your rotary tool with the circle cutter guide and set it to a radius of 42.5mm. Drill a small 1/8" hole right in the center of the mark, place the rotary tool into the board and cut out an 85mm diameter circle. Put an LED strip into the cup holder and ensure that you the have correct diameter, then proceed to cut out the 7 other cup holders. Save the inner wood cutouts for the LED rings. We will split them in half later and use them as jigs to keep the LED rings in place while we silicone them in place.

Step 26: Construction: Cutting Out the Ball Washer Holes

I had cut out the ball washer holes on the table and the ball washer holes on the rails separately. In this step we will actually do it all in one go, this way everything stays lined up perfectly and saves a lot of measuring. I have attached a PDF file to this step which contains the measurements and locations for the screws that we will be putting through the railing and into the table. There is a total of 28 1½" screws used to secure the railing to the table. The outer edges of each railing should be flush with the outside of the table, as well as each end of the railing should be flush with the ends of the table. Once you have it lined up, measure out the screw locations and secure the railing to the table. You will want to countersink each screw hole so that the screws do not protrude above the railing.

Now we need to mark the location for the ball washer holes that we will be cutting out. From the bottom edge of the railing measure up 150mm, then from the outside edge of the railing measure in 150mm and make a mark. The mark will be quite close to the notch if you have measured it out properly. Attach a 2" hole saw to your drill, drill out the ball washer hole and that's it! Repeat the same process for the other 3 ball washer holes. Save one of the 2" wood cutouts as we will use it later with the lexan sheet.

Step 27: Construction: Securing the End Rails

The 6¼"x96"x3/8" pieces of plywood and the 6¼"x96"x7/16" pieces of plywood/OSB are used to make the end rails on the beer pong table. The total height of the outside rails for the table is 15/16" (7/16" + 1/2"), the notch is 1/8" deep which gives us a height of 13/16" at the notch. Our lexan sheet sits in the notch of the side rails and it sits on top of the end rails. The lexan sheet is 1/8" thick, so we must make our end rails 13/16" tall (15/16" - 1/8") so that the lexan can sit on top of it while still being flush with the side rails.

Measure out and mark the screw locations on the end rails using the PDF CAD drawing at the bottom of this step. Get the drill and secure the end rails in place, there is a total of 5 screws per end rail. Each end rail should fit snug between the side rails, be flush with the end of the table and the top of the end rail should be flush with the notch on each side rail.

Once you are done, slide the 2'x8' lexan sheet onto the table. The sheet should fit right in the notches on the side rails and be flush with the top of the rails. If it fits in too tight, you will have to increase the width of the notches on the side rails until it fits in snug but not too tight.

Now with the lexan sheet over top of the end rails, peel back a little bit of the plastic wrap so that you can see the screws on the end rails. Make sure that the lexan sheet is flush on each end of the table and make a mark over top of each screw on the lexan sheet. Take an 1/8" drill bit and drill out each screw hole on the lexan, finish it up by countersinking each hole.

Some of the photos that I used for reference in this step are of the finished table. It turns out that I didn't take the correct amount of pictures that I needed at this step so I had to go back and take them once the table was finished.

Step 28: Construction: Ball Washer Holes and the Lexan Sheet

We have already finished cutting out the ball washer holes on the beer pong table but we still need to modify the lexan sheet to accommodate the holes.

Take the 2" wooden cutout that we had saved from step #26 and line it up with the ball washer hole on the side rail over top of the lexan sheet. Take a marker and trace a half-circle around the wooden cutout on top of the lexan sheet. Now we have an outline of how much lexan we need to cut away in order to install the ball washers. Do this for the other 3 ball washer holes too. Unscrew the lexan sheet from the table and pull it off to the side of the table so that we have enough clearance to cut out the holes.

Set up a jigsaw with a fine-toothed blade and slowly cut out the portions of lexan while following the marker outlines. Go very slow and be really careful here as we do not want to crack or scratch the lexan sheet. Once the ball washer holes have been cut out, put the lexan sheet back on the table and confirm that each hole lines up with the table.

Step 29: Construction: Painting the Rails

Now that we have all of the rails done and our lexan sheet fits snug in between the rails, it's time to remove the rails one last time and paint them. I painted mine red, but feel free to paint them whatever color you would like (I think lime green would look cool too). Once you have each rail painted and have allowed each one to dry, get the can of waterproof lacquer and apply a couple coats of lacquer to each piece allowing each coat to dry before applying a new one.

I had thought about staining the rails and then lacquering them, this way they would have matched the rest of the table. I opted to paint the rails a solid color instead just to change things up a bit. Once everything is painted and lacquered, secure the rails to the table for good as we won't be removing them again. Do not put the lexan sheet back on yet as we have to waterproof around the ball washer holes.

Step 30: Construction: Prepping the Ball Washers

The table is really starting to come together now. We only have a few more things to finish before we can test it out and start playing around with programming it!

Get the two 11/16" long pieces of 3" ABS pipe and cut each one in half. We will then have four halves which we will be using to create a guard around each ball washer hole for waterproofing. Place about 1/8" of silicone on top of each guard and allow it to completely cure. This will create a seal up against the lexan sheet without us having to actually adhere it to the sheet. This way if the lexan sheet gets damaged or too scratched up, it is easy to replace and we don't have to re-silicone each guard when we install a new sheet.

Once the silicone has cured, add fresh silicone to the bottom and sides of each guard and then place the each guard around its respective ball washer hole and press it firmly to the table. Install the lexan sheet over top of the guards and secure it to the table. Seeing as we didn't adhere the silicone on the top part of the guard to the lexan, we can still remove the lexan sheet and replace it without having to re-silicone the top of the guard. Refer to the photos as they are very helpful for this step.

Step 31: Construction: the Ball Washer LED Rings

In this step we're going to add the LED rings to the ball washer entry and exit points. The LED rings serve two purposes, first, they cover up the roughcut around the lexan and plywood rails for each ball washer hole, and second, they provide a better seal around the ball washer holes. It doesn't really matter if water or beer gets around any of the ball washer holes as they are sealed from the table, but the less water and beer that can leak into places, the better it is.

The first thing that we need to do is trim each LED strip. The LED strips consist of 24 LEDs that are wired with 3 LEDs in a series wiring scheme and 8 of these schemes in parallel to each other. We can cut apart the LED strips as long as we do it in sections of 3 LEDs. In order to put an LED ring around each of the four 1.5" ABS couplers, we need to cut off the last 3 LEDs on each LED strip that goes around the couplers. Once we have trimmed down the LED strips, we need to make a notch right below the wire that comes out of the LED strip. We can then put the wire inside of the notch and route it so that the wire comes out facing the floor. Next, put a dab of super glue on each end of the LED strip and bend it into the shape of a ring. The ends should come together and be nice and flat against each other, hold the ring together until the glue dries.

Once the glue has dried, use a generous amount of silicone to line the inside of the LED ring. Now put the ring over top of the 1.5" ABS coupler and make it so that the top of the LED ring (not the LEDs, the clear silicone filling part of the ring) is flush with the top of the ABS coupler. Build a small jig to hold the LED ring in place until the silicone can dry. Add more silicone if needed, we don't want any holes or missed spots where the LED ring and coupler join together.

Once the silicone has cured, cut off an 8' piece of CAT5 and strip away the PVC jacket. Separate each pair of wires and then solder one pair of wires to each LED ring on the ball wash couplers. The wires on the LED rings aren't near as long as we need them to be, so we are just extending the length of these wires so that they can reach the main PCB. Once the wires are soldered on, add a 0.1" 2-Pin polarized connector onto the end of each wire. Use the photos for reference.

Step 32: Construction: Making the IR Sensors for the Ball Washers

The IR sensors for the ball washers are relatively easy to make. The first thing that we need to do is cut off two 8' pieces of CAT5, strip the PVC jacket off of each line and separate each pair of wires. Each sensor will need two pairs of wires to connect to the TCRT5000 IR reflective sensors, a total of eight pairs of wires for four ball washer sensors.

Trim the leads on the TCRT5000 sensor and bend them up at a right angle making sure that they do not cross paths. Take one pair of wire, add a section of heatshrink to each strand and then solder one wire to the anode and one wire to the cathode of the IR transmitter. Take the other pair of wire and repeat the same process for the IR receiver. Slide the heatshrink down and use a lighter to shrink it around the leads. Do the same thing for the other 3 ball washer sensors while making sure to keep track of which wires are connected to what leads on the TCRT5000 sensor.

Step 33: Construction: Installing the IR Sensors in the Ball Washers

Installing the IR sensors on the ball washer couplers is pretty easy. Drill a 3/8" hole underneath the LED ring on the coupler. The hole should be on the opposite side of where the LED ring wire is on the coupler. Once you have the hole drilled out, fit the IR sensor into the hole while making sure that it does not protrude too far into the inside of the coupler. When you have it aligned properly in the hole, put a generous amount of hot glue around the sensor and hold it in place until the glue dries.

As with the other steps, repeat this process for each ball washer coupler. I never said that this table didn't require any tedious work!

Step 34: Construction: Building the Ball Washer Fans

The items that we need to make two ball washers are listed below.

2x 55mm 6-Blade Duct Fans
2x 24mm 13000 RPM 12V Electric Motors
2x 1.5" ABS T-Couplers
2x 24" Lengths of 5/16" hose
2x 8' lengths of twisted pair cable (Separated CAT5 wire)
6x 0.1µF Ceramic Capacitors
Electrical Tape

The first that we need to do is assemble the fan. Install the 24mm electric motor inside of the fan casing. Use the two shallow machine screws (I had to cut them down a little bit more with a hacksaw) that come with the fan assembly to secure the motor in place. Once the motor is secured, take the fan blade, slide it onto the motor shaft and use the hex key to tighten the blade to the shaft of the motor. The blade should now spin freely in between the casing when you rotate it with your hand.

Flip the fan over, solder three 0.1µF capacitors to the motor to help suppress the electrical noise that the motor will create. One capacitor will go from the positive terminal of the motor to the outside casing of the motor, the next capacitor will be soldered between the positive terminal and negative terminal of the motor and the last capacitor is soldered from the negative terminal of the motor to the motor casing. This is a pretty crucial step as it may cause the RGB pods to flicker (noise on the SPI bus) if we don't suppress the noise that the two ball washers create. Next, solder the white/blue CAT5 line to the positive terminal and the solid blue line to the negative terminal of the motor.

Once the motor has the wire and capacitors soldered to it, put a wide piece of tape or plastic over top of the connections to prevent water from the ball washer getting into the motor. Then make a small notch on the back-end of the fan casing so that we can route the motor wires through it. Place the T-coupler up against the back-end of the fan casing and use electrical tape to join the two parts together. We now have a completed fan and are that much closer to getting the ball washers completed!

EDIT

Another option for the fans are to use DC 3.7V coreless motors with a propeller from eBay. My circuit runs +12V to the motors, so you would need to reduce the PWM duty cycle that controls the motors to something near 3.7V. There is 4096 steps and +12V is the power supply to the motors, so (12V / 4096) steps is equal to ~2.93mV per step. To get 3.7V, we would set the PWM duty cycle for each fan motor to a value around 1263 (3.7V / 2.93mV per step). One would then have to create a jig to hold the motor and propeller in place underneath the T-Coupler where the duct fan would have went. If I was to redo my table, I would use these motors instead as it is much cheaper and they are smaller. Using these motors with propellers included would cost $3.59 total (you still would have to make a jig to hold the motor though), while the two ducted fans and the two motors that I originally used cost ~$25 altogether. Click here for the link to find them on eBay.

Step 35: Construction: Installing the Ball Washers

Take a 2L plastic pop bottle and cut the back end off of it so that you have an open bottom that is flat. This is where the water will strain into, when the water is dirty we can simply unscrew the cap and the water will drain out. We then would put the cap back on and fill it up with clean water. Next, make four 3/4" cuts evenly around the bottom of the pop bottle. We can then bend back the plastic in between each cut to create a square lip around the bottle.

Now we need to make a slit in the pipe with a few holes to allow all of the water to strain out before reaching the end of the pipe and going into the fan. This part isn't crucial and doesn't have to be done exact, just as long as all of the water drains out when it is done. Measure 3/4" into one side of the pipe, make three 3/8" holes next to each other in a horizontal line (use picture for reference). Go up one row and make two more 3/8" holes centered between the three holes below it. Above the two holes make a 3/16" slit across the bottom of the pipe and above that make three more 3/8" holes that are in line with the first three holes that we drilled.

Now we will cut a 2" hole on the front of the plastic pop bottle and another 2" hole directly opposite of the first hole on the other side of the bottle. The second hole should be a bit lower than the first hole, this way the pipe will go through the bottle at an angle which will allow the water to drain. Make a 3/8" hole 2" down from the cap of the bottle. Put the submersible water pump inside of the bottle and route the electrical wires through the 3/8" hole. Slide the 5/16" hose in through the same hole and hook it up to the water pump. Silicone around the hole on the inside and outside of the bottle to prevent any water from leaking out. While the silicone is curing, drill a 3/8" hole near the bottom of the 90° coupler for the water hose.

Once the silicone has cured, assemble the ball washer and place it underneath the table. Facing the middle of the table, the entry point of the ball washer goes to the left hole (90° coupler) and the exit hole (T-coupler & fan) goes to the right. Take two pieces of steel strapping and secure the ball washer to the table using 3/4" screws. Route the hose coming out of the plastic pop bottle across the pipe to the entry coupler and super glue it into the hole. Now we can install the LED ring couplers!

Step 36: Construction: Installing the LED Ring Couplers

We only have two more things to do until we can test out the ball washers! First, we need to install the LED ring couplers (the ping pong ball entry and exit points) and then we need to place connectors on the IR sensor wires.

The entry point for the ball washer on each side of the table is to the left of the players. At the entry point on the master side of the table, take a LED ring coupler and a 20mm length of 1.5" ABS pipe and fit the two together. Feed the LED ring wire and the IR sensor wires through the ball washer entry hole on the table and pull the line tight. Snap the LED ring coupler onto the 90° coupler and press it firmly against the top of the table. The entry point of the ball washer on the master side is now completed.

Next, go to the exit point of the ball washer (to the right of the player) and attach the 40mm length of 1.5" ABS pipe to the LED ring coupler. Feed the wires from the LED ring coupler through the ball washer exit hole and join the LED ring coupler to the T-coupler which contains the blower fan. Press the LED ring coupler firmly against the table and now we have completed the exit point of the ball washer on the master side of the table.

The last thing that we need to do is connect up an RJ45 connector to the IR sensor wires. The TX lines control the infrared transmitters on each IR sensor, they are connected together on the PCB and are not independent. The IN_RX line controls the IR receiver and detects when a ping pong ball enters the ENTRY point of the ball washer. The OUT_RX line controls the IR receiver and detects when a ping pong ball goes through the EXIT point of the ball washer. Between the two ball washer sensors (entry and exit) there are eight IR sensor wires, these are both combined into one RJ45 connector. Pins 1 - 4 represent the ENTRY IR sensor and Pins 5 - 8 represent the EXIT IR sensor. There is a connection diagram in the photos.

On the entry IR sensor on the master side, I connected the blue pair to the IR transmitter and I used the orange pair for the IR receiver. On the entry IR sensor on the secondary side I connected the green pair to the IR transmitter and I used the brown pair for the IR receiver. My wiring scheme is outlined below.

RJ45Pin - Wire - TCRT5000 Pin
1 - Orange/White - Phototransistor Collector (Entry Sensor)
2 - Blue/White - Transmitter Anode (Entry Sensor)
3 - Orange - Phototransistor Emitter (Entry Sensor)
4 - Blue - Transmitter Cathode (Entry Sensor)
5 - Brown/White - Phototransistor Collector (Exit Sensor)
6 - Green/White - Transmitter Anode (Exit Sensor)
7 - Brown - Phototransistor Emitter (Exit Sensor)
8 - Green - Transmitter Cathode (Exit Sensor)

Once you have the connector crimped on, plug it into the Ball Washer Control port on the RGB Pod Controller for that side of the table. As for the ball washer LED rings, the connections are as follows:

Master PCB Port - Ball Washer LED Ring
LED Ring #9       - Ball Washer Entry (Master Side)
LED Ring #10     - Ball Washer Exit (Master Side)
LED Ring #11     - Ball Washer Entry (Secondary Side)
LED Ring #12     - Ball Washer Exit (Secondary Side)

Now we just have to connect up the ball washer pumps and fans. Refer to the master PCB schematic for clarification. CON13 connects up to the blower fan for the master side ball washer (BW #1), CON14 connects up to the water pump for BW #1, CON15 connects up to the blower fan for BW #2 and the water pump for BW #2 connects up to CON16. I have created a diagram for reference.

Now we can test it out!

Step 37: Construction: Testing the Ball Washers

In this step we will test out the ball washers. I have included the test code in a downloadable zip file at the bottom of this step. The ball washers are fairly simple, there is an entry hole where a player drops a dirty ping pong ball into, an infrared sensor will then see the ball in the pipe and start the water pump. The water traveling down the pipe will push the ball with it, at the end of the pipe the water is strained out and the ball keeps moving, the pump is then shut off and the blower fan is started up to blow the ball out of the pipe. You can just run the code as it is, I won't get into the software side of things right now as that is taken care of in step #57.

Step 38: Construction: Installing the LED Rings

Prep the LED rings the same way that we did for the ball washer LED rings (step #31) except do not cut the LED strips down at all. We need all 24 LEDs on the strip in order to fill the diameter of the circle. Spray frosting over top of the LED rings in order to diffuse them, make each strip into a circle and super glue each end together. If you are having troubles forming the LED rings into a circle, you can heat up each strip with a heat gun and then they will form much easier.

Next, drill a 1/8" hole in each of the eight LED ring cutouts on the table. The hole should go completely through the table to the bottom so that we will be able to route the LED ring wires underneath. Take care not to drill through any of the LED matrix lines underneath the table.

Now take some silicone and silicone all around the perimeter of the cutout but not over top of the 1/8" hole that we just drilled. Take the LED ring, feed the wire through the hole to the bottom of the table and firmly press the LED ring into the cutout on the table. Build a small jig to hold the LED ring in place while the silicone cures. I used half of a wood cutout from the table with a length of staples to press it tight against the ring. Staples work great because you can get the perfect fit that you need, just keep removing staples from the staple strip until it is the length that you need.

Just as in step #31, add an 8' length of wire to each LED ring and place a 2-pin 0.1" connector on the end of each wire in the same fashion as the ball washer LED rings.

Step 39: Construction: Testing the LED Rings

First we need to connect up the LED rings to the Master PCB. Photo #2 contains a diagram which shows the placement of each LED ring, cross-reference that with photo #3 and you can see where each ring connects on the Master PCB. Photo #3 does not show the ball washer LED rings connected up, but the connectors go in order from LED ring #1 to LED ring #12.

Now it's time to test out the LED rings! Just like the other steps, there is a downloadable zip file that is at the bottom of this step. Extract it, start up the MPLAB IDE, build the project, program it and watch the beer pong table cycle its LED rings through three different animations.

This video has the RGB pods, the LED grid and the LED rings active. In the example code for this step, only the LED rings will be active.

Step 40: Construction: Assembling the PCB Panel

The PCB panel is a 10"x20"x½" piece of plywood that we will mount the master PCB, the LED grid controller, the two RGB pod controllers and the power supplies too. We will mount it to the bottom of the table and place it in the middle, between the legs of table.

You can lay out the PCB panel whichever way that works best for you, I put my power supplies at the bottom of the board, the master PCB in the center, the LED grid controller just up from the +5V PSU and the two RGB pod controllers at the top of the board. I used 3/4" screws around each PCB to secure them in place, not exactly ideal, but it works. Once the PCB panel is mounted to the table we can begin cleaning up the wiring!

Step 41: Construction: Routing the Many Cables

The easiest way to route the cables underneath the table is to bunch groups of them together with zipties and navigate each bundle to the center of the table where the PCB panel is located. Make a bundle of cables for the LED grid wires, two bundles for the RGB pods (master and secondary side) and then run the separate LED ring wires and the ball washer wires last, up alongside one of the larger bundles. It doesn't have to be ridiculously neat, we just don't want any loose wires hanging down from the table.

You will have excess lengths of cable once you run each bundle to the PCB panel, just coil up the excess cable neatly and secure it to the table with a ziptie and a 3/4" screw. For the single pair wires such as the LED rings, I looped the excess wire around two spaced out screws in order to keep it neat. Refer to the photos as needed.

Step 42: Construction: Siliconing the Lexan Sheet

In this step we will finalize the construction of the table by siliconing the lexan sheet to the table. The silicone does two things, first, it will create a seal so that liquid can't leak on to the inner part of the table and second, it will adhere to the lexan sheet and help hold it in place.

Remove the ball washer LED rings from their respective holes and remove the lexan sheet from the table. Make sure that the inside of the table below the lexan sheet is completely clean and that everything looks how you want it too. After this step, the lexan sheet will be much more difficult to remove as it will be siliconed to the table. Take the silicone tube and put a small bead in each notch on the side railings, then lay a bead of silicone above the screws on each end rail.

Wipe down the underside of the lexan sheet to make sure that it is clean and streak-free, then place the lexan over one of the side rails (not in the notch yet). Line up the sheet with each end of the table and set one side in its respective notch. Carefully lower the other side of the sheet that you are holding and place it into the notch on the other railing. Take a good look at the sheet and adjust it if is off target. Once it is perfectly in place, put the screws back in on each end rail to secure the lexan in place and add small weights across the edge of the lexan sheet to keep it firmly pressed against the silicone. Allow the silicone a full day to cure.

Step 43: Software: an Introduction to the Software

Now that we have finished all of the construction, we can begin learning how everything works! This is where it gets really fun as there are so many animations and effects that one can make with this table. Before we move on to making our own animations, it is beneficial to learn how data is sent amongst the different chips, how the interrupt routines have been set up, certain time constraints, etc.

There is a fair share of source code to sift through, whether it be packing data for the RGB pods, running animations on the grid or controlling the ball washers. In every C file I have ordered all of the functions alphabetically so that one can find the piece of code that they need quicker. I could have split the code up into smaller files but I chose to keep the feature controls and the main animations grouped together, most of which is contained in the Miscellaneous.c and LED_Graphics.c files.

I have attached a downloadable zip file to this step which contains the final source code for the beer pong table. The code is set to run through ten different LED grid animations, six different RGB pod animations and three LED ring animations all at the same time while monitoring the ball washers. All of the animations that you learn from here on in is contained in the source code downloaded from this step. This code does NOT poll for an RF signal though, you can adjust it to do so or download the example project file in step #82. All of the zip files that you have downloaded from previous steps contain the exact same source files as one another, only the main function is calling different functions in each step. You can just use one project file from an earlier step and update the main() routine with the code that you want to run or download each project file, whatever is easier for you.

Now it's time to step through each function of the table one by one, completely understanding one feature before moving on to the next. It is the same thing that we just did in the last 35 steps with construction, only this time we are doing it with software. First off, we will learn how to send data to the LED grid and display whatever we wish on the 32x12 pixel array.

Step 44: Software: Breakdown of the HT1632C LED Drivers

You had probably noticed when you were building the LED grid controller that there weren't many components on the PCB. This is because the two HT1632C LED drivers handle everything that we need. We just needed to add a few current limiting resistors for the LEDs and the appropriate connectors for the control and power lines.

The LEDs that I used are 3.0V and can operate up to 20mA. The HT1632C drivers are operating at 5.0V and will push 13.3mA through each LED when it turns them on. The 150Ω network resistors could actually be reduced lower to allow more current to be pushed through, however, I like to play it on the safe side. With the LEDs being multiplexed and still well below the 20mA limit, we have nothing to worry about.

The HT1632C has four main control lines, chip select (CS), read (RD), write (WR) and data (DATA). CS has to be pulled low to enable the data and control lines, the read control (RD) line is used to signal to the LED driver that we will be reading data from its RAM, we don't use the read command anywhere in my code as we will just keep track of what we write to the LED grid. The write control (WR) line is used to signal to the chip that we will be writing data to it. The DATA line is used to send data to the HT1632C or to receive data from the HT1632C.

The HT1632C can be configured as a 32x8 LED driver or a 24x16 LED driver. I technically could have used one driver to control all 384 LEDs (24 * 16 = 384) but the software and data packing would have became a lot more confusing. Being such a large project, I had to keep things as simple as possible. Therefore I opted for two HT1632C drivers each configured as a 32x8 LED driver. The second HT1632C is configured as a 32x8 driver yet we only use it as a 32x4 LED driver seeing as we do not need the extra 4 columns.

Aside from configuring each chip as a 32x8 LED driver, one of the drivers has to be designated as a master and the other a slave. We then have to turn off the system oscillator and set the COM option to an N-MOS open drain output and 8 COM option (page 22 of the datasheet). We are just configuring the drivers so that they will work with the way that we set up our 32x12 LED matrix. In the next step we will actually take a look at the code and see how simple it is to control each LED pixel.

Step 45: Software: How the LED Grid Data Is Packed

Each pixel in the LED grid is represented as one bit in the global unsigned 16-bit variable LED_data[32]. The first thing that you will notice is that the LED grid contains 32 rows and LED_data[32] is a 32 WORD array (a WORD is a 16-bit variable).

Looking at the grid in the first photo, you will see that ROW0 is represented by LED_data[0], ROW1 is represented as LED_data[1], ROW2 is represented as LED_data[2], all the way up to ROW31 that is represented as LED_data[31]. Since each LED_data[x] WORD is 16-bits, we can represent the 12 columns with 12 of those 16-bits (the 4 MSb's aren't used). For each LED_data[x] WORD, COL0 is represented by bit 0, COL1 is represented as bit 1, all the way to COL11 which is represented as bit 11.

The ROWs of the grid are represented as the x-plane and the COLs of the grid are the y-plane. If we wanted to turn on the pixel at location (0,0) we would set LED_data[0] = 0b000000000001 (0x001). When the grid updates it will see that bit 0 of LED_data[0] is equal to 1 (ON) and send the required data to the HT1632C to turn on the pixel.

Let's do the example in photo #2.
We only want to turn on the pixel at ROW11 and COL7. We would navigate to LED_data[11] and set bit 7 equal to 1 and then update the grid. It's as easy as that.

LED_data[11] = 0b000010000000 = 0x080

Here are the values for the example in photo #3 (I have only listed values for the ROWs that have one or more pixels that are on).
LED_data[3]   = 0x004
LED_data[7]   = 0x080
LED_data[11] = 0x210
LED_data[16] = 0x040
LED_data[18] = 0x020
LED_data[22] = 0xFFF
LED_data[29] = 0x1C0

Now that we know how the data is mapped to our LED grid, we can learn how to update the grid!

Step 46: Software: How the LED Grid Is Updated

The LED grid is updated through an interrupt routine every 8ms (125Hz). When we modify the LED grid, we don't update the grid right at that point in time, instead we set a flag (HT1632_UPDATE) and on the next interrupt cycle the LED grid will be updated. If the LED grid does not need to be updated and the interrupt occurs, it will first check HT1632_UPDATE and see that it is cleared and it will just continue on with the program and not write anything to the LED grid controller until the next interrupt occurs and it has to check again to see if there is an update.

The best way to explain how to update the grid is to use a small example. If we wanted to light up all of the LEDs in ROW7 and have all of the rest of the LEDs off, we would write a function such as this.

void Turn_On_ROW7(void)
{
  int i;

  //Clear each LED in all 32 ROWs
  for (i = 0;i < 32;i++)
      LED_data[i] = 0x000;

  //Turn on all 12 of the LEDs in ROW7
  LED_data[7] = 0xFFF;

  //Set the update flag so that the grid will be updated on the next Timer3 interrupt
  HT1632_UPDATE = 1;
}

As soon as Timer3 interrupts, it will see that HT1632_UPDATE is set and call LED_Refresh_Grid(&LED_data) to update the grid with the new data that we have just put into the LED_data[x] array. You can update the LED grid directly (by calling LED_Refresh_Screen(x) directly from the function) and override the interrupt, but for the most part it is not necessary.

Step 47: Software: LED Grid Functions

I have created a few basic functions to be used with the LED grid. Before we start making any animations, I will walk you through these functions so that you have a better understanding in regards to how they work. Each basic function will only modify the LED data, it won't actually write it to the grid. When you want to write the modified data to grid, you need to call a grid update (HT1632_UPDATE = 1).

void LED_Pixel(UINT8 px, UINT8 py, UINT8 state)
This function takes an (x,y) coordinate and modifies the bit that the pixel represents. If 'state' is equal to 1, the bit that the pixel represents will be set to a 1, if state is equal to 0, the bit will be cleared to 0. Back in step #45 in example #2, we had to modify the LED data to turn on Pixel(11,7). If we were to use this function to turn on the LED at (11,7) we would write:

LED_Pixel(11,7,ON);
HT1632_UPDATE = 1;

The reason that I don't update the pixel in the LED_Pixel(x,ystate) function itself is because we may want to modify multiple pixels and have them all update at the same time.  Such as this:

LED_Pixel(16,5,ON);
LED_Pixel(16,6,ON);
LED_Pixel(16,7,ON);
LED_Pixel(16,8,ON);
LED_Pixel(17,8,ON);
LED_Pixel(18,8,ON);
HT1632_UPDATE = 1;

Even though we have called LED_Pixel(x,y,state) six different times, the modified LED data will all be updated at the same time when the next timer3 interrupt occurs. In case you were wondering, those pixels make up an 'L' shape on the grid.

void Draw_Circle(UINT8 px, UINT8 py, UINT8 radius)
This function will modify the LED data and create a circle around the center point of (px,py). The 3rd parameter will set the radius of the circle. Photo #2 shows an example for the following code:

Draw_Circle(16,6,3);
HT1632_UPDATE = 1;

void Draw_Rect(UINT8 px, UINT8 py, UINT8 sx, UINT8 sy)
This function is used to draw a rectangle starting at point (px,py). The size of the rectangle is specified in pixels by sx and sy. Photo #3 shows an example of how the rectangle is drawn (a square can also be created with this function).

Draw_Rect(5,2,8,6);
HT1632_UPDATE = 1;

void Fill_Grid(void)
This function will set every bit in the LED data array to 1. If the grid is updated after calling this then all of the LED pixels will turn on.

Fill_Grid();
HT1632_UPDATE = 1;

void Clear_Grid(void)
This function will clear every bit in the LED data array to 0. If the grid is updated after calling this then all of the LED pixels will turn off. This function is useful when we want to draw a new frame on the LED grid, we can reset all of the old LED_data[x] bits and start modifying the data knowing that each bit has been reset to 0 (OFF).

Clear_Grid(); //Clear out old data
//Write in new data here
HT1632_UPDATE = 1;

void Draw_Border(UINT8 width)
This function will draw a border around the perimeter of the LED grid. The width of the border is set by the value passed into 'width'. The maximum value for a border is 6 pixels (6 pixels * 2 sides = 12 COLs) and the minimum value is 1 (obviously). The example below is demonstrated in photo #4.

Draw_Border(2);
HT1632_UPDATE = 1;

void Invert_Grid(void)
This function will simply invert each of the LED data bits. If you run this function right before you set HT1632_UPDATE equal to 1, it will display the exact opposite of what it was going to display before running this function. (i.e. the LEDs that were on will be off and the LEDs that were off will be on).

//Other code setting LED bits here
Invert_Grid();
HT1632_UPDATE = 1;

void HT1632_Set_PWM(UINT8 value)
This function is in the HT1632C driver files and it can set the LED grid to 16 different levels of brightness. Photo #5 shows the duty cycles for each brightness and photo #6 shows the duty cycles of each brightness in a graph form. This function does NOT need a grid update after it is called as it writes directly into the HT1632C drivers and adjusts the brightness through PWM. If we wanted to set the LED grid to dim down to a 10/16 duty cycle (refer to chart for value) we would write:

HT1632_Set_PWM(9);

That's the majority of the basic functions. Now that you know how to control the LED grid we can combine these functions together and use time delays to make some really cool effects.

Step 48: Software: Time Delays and Animations

In order to make an animation, we will need to create some form of a time delay between frames, otherwise the animation will only take a split second to complete. We don't want to use any conventional time delays that will halt the program and render other features such as the ball washers useless. We need to keep the program looping through all of the code and checking each segment of code to see if the elapsed time has passed.

One of the most effective ways to do this is to use a global counter that gets incremented in an interrupt routine. In the interrupt service routine for Timer3, there is a global 32-bit variable (count32) that gets incremented on every interrupt. This variable will be used in the majority of our interrupt delays along with a function called 'Time_Check(UINT32 *mark, UINT16 interval)' to allow us to keep track of the delay time while still keeping the program flowing. We will create an example animation that lights up each row on the LED grid one at a time. We will call it 'Our_Test_Animation(void)'.

Our_Test_Animation(void)
1) In the Globals.h file, dedicate seq[x] to your function that you are creating. 'x' being an unused variable in the seq[] array. For this example we will use seq[24].

2) Add the function prototype to your header file and set up the function in the C program file. For this example, ours will be 'UINT8 Our_Test_Animation(void)'.

3) Declare a 'static UINT8 last_seq' variable that is equal to 0xFF and a 'static UINT32 tmark' variable that is equal to 0. These variables have to be static so that the routine remembers their values when it loops back through the animation.

4) We will need at least one local variable called 'delay' that is used to reference 'count32' in the Time_Check() function. 'delay' does exactly what it's called, it delays the program a specified amount of time (8ms/interrupt * value of 'delay').

5)The reset value for seq[x] is 0xFF. When the routine begins it will execute the 'if (seq[24] == 0xFF)' piece of code, this is where we set up certain variables for the start-up of our animation.

6) The next 'if (seq[24] != last_seq)' statement checks to see if there is a new sequence. If there is, it will update last_seq to the current seq[24] value, it will then update our timing reference (tmark) and then update the animation code.

7) 'if (Time_Check(&tmark,delay))' checks to see if the specified amount of time has passed since the last loop through the function. If it has, it will update seq[24] to the next sequence and the animation will be updated on the next loop through of the function.

8) 'if (seq[24] > 31)' will check to see if all of the sequences have been executed and if the animation has finished. The value of '31' depends on how many sequences you have in your routine (we have 32 sequences in this routine, 0 - 31). If all of the sequences have been executed, we will set seq[24] to its default value of 0xFF and return a 0 indicating that the animation has finished.

9) If the animation hasn't executed all of its sequences it will return a 1.

Cross reference the above steps with the finished code in photo #4. By creating an animation this way, we are limited to 255 sequences (255 sequences + 1 reset sequence) because we are using an 8-bit variable for seq[x]. If you need more than 255 frames, you can change seq[x] to a 16-bit variable and have up to 65535 sequences. If you need more precise timing than Timer3 can offer (~8ms per interrupt), you can set up Timer5 to interrupt sooner and move the global counter (count32) there. Lastly, the default amount of animations that the code can support is 50. If you need more than 50 interrupt delayed animations, you just need to increase the value of the constant SEQ_AMOUNT (typedefs.h) to the value that you need.

The main limitation to using an interrupt delay such as this is that our main loop has to be efficient and we can't waste too much time in any routine in the main loop. The program must keep flowing the whole way through because if it halts for too long it will delay the timing of the rest of the routines along with it. Photo #5 contains another example for an animation called Cycle_Colors(void) that uses the same interrupt delay format.

Step 49: Software: Breakdown of the RGB Pods

There are a total of five TLC5940 PWM drivers daisy chained together on the beer pong table and they control the RGB pods, the LED rings and the ball washer motors. Each pod has four RGB LEDs with three separate channels (red, green and blue) to control and each of these channels are controlled by one 12-bit PWM output on a TLC5940 driver. In order to drive the channels on each RGB pod, we have to dedicate three PWM channels to each pod and then organize a data array so that we are able to control the PWM level of each output individually, thus giving us a full spectrum of colors. The first four TLC5940 drivers control the RGB pods and each driver controls 5 pods for a total of 20 pods. The last TLC5940 controls the twelve LED rings and the ball washer motors and only dedicates one PWM channel to each of those features.

Each TLC5940 needs 192 bits of data (24 bytes) to operate. To control all five of the TLC5940 chips, we use an 8-bit global variable array called RGB_data[120] that contains 120 elements (24 bytes per TLC5940 * 5 TLC5940s = 120 bytes). The bit data is shifted most significant bit (MSb) first into the TLC5940s by the SPI module and we shift the RGB_data[120] array LSB (Least Significant Byte) first, meaning we shift out element 0 of the data array first and continue consecutively up to element 119 of the data array. TLC5940 #5 will have the first 24 bytes of data that were shifted out from the microcontroller and TLC5940 #1 will have the last 24 bytes that were shifted out from the microcontroller.

I have attached a photo which shows what location of the array pertains to which TLC5940 and what features that driver chip controls on the beer pong table.

TLC5940 #1
This chip is located on the Secondary Pod Controller PCB and is labelled as IC1 on the schematic. Array elements 96 to 119 are the 24 bytes that control RGB pods #16 - #20. All of the array elements for the other chips will pass through this TLC5940 as it is first in line.

TLC5940 #2
This chip is located on the Secondary Pod Controller PCB and is labelled as IC2 on the schematic. Array elements 72 to 95 are the 24 bytes that control RGB pods #11 - #15.

TLC5940 #3
This chip is located on the Master Pod Controller PCB and is labelled as IC1 on the schematic. Array elements 48 to 71 are the 24 bytes that control RGB pods #6 - #10.

TLC5940 #4
This chip is located on the Master Pod Controller PCB and is labelled as IC2 on the schematic. Array elements 24 to 47 are the 24 bytes that control RGB pods #1 - #5.

TLC5940 #5
This chip is located on the Master PCB and is labelled as IC2 on the schematic. Array elements 0 to 23 are the 24 bytes that control the twelve LED rings and four ball washer motors.

Step 50: Software: How the TLC5940 Works

In this step, I will give you a brief overview of the TLC5940 16 channel PWM driver. All of the steps needed to control the TLC5940's are done automatically in code when you write to the RGB pod control functions, however, it is good to know what is going on behind the scenes in case you ever need to modify this part of the code.

Each TLC5940 adds sixteen 12-bit PWM outputs to our circuit which we combine to control the RGB pods and to control the LED rings and ball washers. The TLC5940 gets its data shifted in serially so we need to control data, clock and other control signals in order for the driver to operate properly. Pin 18 is the GSCLK signal which needs a high frequency PWM rate to keep the outputs updated, for this we will sacrifice the PWM2 module on the microcontroller. A breakdown of the pins are below:

XLAT: Used to latch the data into the TLC5940's after all of the data has been shifted in.

BLANK: Marks the end of a PWM cycle. When pulled high it will disable all of the outputs, when pulled low it will re-enable the outputs and start a new PWM cycle.

GSCLK: Controls the clock rate for the PWM cycle. We use PWM2 on the microcontroller to update this at a rate of 250KHz.

DCPRG: Selects the source of the current limit register. This is used in Dot Correction Mode.

VPRG: Used to select the current limit registers or the duty cycle registers for writing.

XERR: We do not use this pin. It will let you know if the chip is overheating or has a burnt out LED.

SCLK: Keeps each chip synchronized while shifting data.

SIN: This is where the data gets shifted into the TLC5940.

SOUT: This is the serial data out from the TLC5940. This connects to the next cascaded TLC5940's SIN input which allows us to daisy chain multiple TLC5940's together.

In my code, I have packed the data that is shifted into the TLC5940's in such a way that we can use one of the SPI modules in the microcontroller to send the data. This is a huge plus as SPI can send data much faster than if we were to do it in software with bit banging. Here is a breakdown on how to get a TLC5940 configured and up and running:

1) Run TLC5940_Init() to initalize all of the pins and flags.

2) Enable PWM2 for GSCLK running at a frequency of 250KHz. We refresh the TLC5940's at 60Hz (16384μs per refresh) and each output has 4096 steps (12 bits).

GSCLK Refresh Rate = (60Hz * 4096 steps) = 245760Hz = ~250KHz

3) Enable all of the timer interrupts that update the TLC5940s. This is done by calling Modules_Init().

4) Set the dot correction for each TLC5940 by calling Dot_Correction(). This sends 96-bits for each TLC5940 and adjusts the amount of current that comes from each output. I just use the default values (0x3F).

5) Set the initial grayscale data. After this routine, we will update the grayscale data automatically with SPI2 through XLAT_Interrupt(), but the first grayscale data routine has to send one extra bit to each TLC5940. Since we cannot modify the SPI module to send one extra bit, we do all of this in software by calling Set_Initial_Grayscale().

6) After the initial grayscale has been set, we can set up the second SPI module and our XLAT_Interrupt() routine will begin sending data with the SPI protocol. We call SPI2_Init() at this point.

7) The TLC5940s are now completely operational and can have data written to them. Before I start my main loop of code I also run a function called Reset_All_Variables() which resets all of the flags and global variables that we use for the beer pong table. This way we know what data is stored in all of the variables at start up.

Feel free to look up the TLC5940 datasheet as it goes much more in depth than I just did. As I stated before, it's good to know how the chip operates but it's not crucial in this project as the data transmission and signal controls are handled automatically in the interrupts.

Step 51: Software: How the RGB Data Is Packed

Photo #3 contains an example and shows how the data is packed into RGB_data[120]. Reference this with the 'Packed RGB_data[n] Chart' and the 'RGB_Packed_Data Pod Layout' photos for help. The RGB_data[n] chart shows what data is located at every 4 bits in RGB_data[24] to RGB_data[119]. Each TLC5940 gets 192 bits shifted into it and for TLC5940 #1 - #4, the first 12 bits (controls OUT15) are not used. This is visible on the Pod_Control schematic as there is no line connected to OUT15 on either four of the RGB controlling TLC5940s. There is a total of 96 bytes used to control the RGB pods (RGB_data[24 - 119]) and the other 24 bytes (RGB_data[0 - 23]) are used for TLC5940 #5 which controls the LED rings and the ball washer motors.

The packed data for the RGB pods can be a little confusing to follow since each channel is 12-bits wide and we are packing each channels PWM value into 8-bit segments. There are two different formats for the data packing because the data gets offset when we are packing all of the data into the array. If you compare RGB pod #5's data structure to that of RGB pod #2's, you will see that RGB pod #5 starts in the middle of a byte whereas RGB pod #2 starts with a full byte.

If you are having troubles figuring out how the data is packed do not worry. Every function that we use to address the RGB pods runs through the function 'RGB_Pod(UINT8 pod, UINT16 red, UINT16 green, UINT16 blue)' which will take care of all the data sorting for us! This is just good to know if you want to manually change the data for whatever reason.

Step 52: Software: How the RGB Pods Are Updated

The TLC5940 drivers are updated at 60Hz through the Timer1 interrupt routine, so every 16384µS a function called XLAT_Interrupt(void) will run and check to see if any of the data in the TLC5940 chips need to be updated. The three main functions that are used to controls the RGB pods are:

void RGB_Pod(UINT8 pod, UINT16 red, UINT16 green, UINT16 blue)
This function will take the parameter 'pod' which should be a value from 1 - 20 and set the corresponding red, green and blue values to the respective channels of that pod. There is no fading in this function, it will simply change the pod to whichever color you wish.

//Change pod #5's color to RED
RGB_Pod(5,4095,0,0);

void Pod_Set_Color(UINT8 pod, RGB pod_color)
This function is the exact same as RGB_Pod(p,r,g,b), only it uses an RGB struct to set the color of the pod. This is nice if you quickly want to to set the pod to one of the default 10 colors.

//Change pod #5's color to RED
Pod_Set_Color(5,COLOR[RED]);

void Fade_To(UINT8 pod, RGB OLD, RGB NEW, UINT16 delay);
This function is by far my favorite as it allows the user to fade in and out of colors. This provides a nice smooth transition from the color OLD to the color NEW and the speed of the transition is controlled by adjusting 'delay'. In my code, I have a global struct array called 'PODn[20]' which is used to save the current value of each pod. For PODn[x], 'x' will always be 1 less than the value of the pod that we are writing to because the pods are addressed from pod #1 - #20 and the array is PODn[0-19]. The format for this function is as follow:

Fade_To(POD#, PODn[POD#-1], COLOR, delay);

Here is an example of fading a pod from red to blue:

//The pod is already red so PODn[4] is already equal to COLOR[RED]
Fade_To(5,PODn[4],COLOR[BLUE],10);

Fade Rates and Fade Periods
You only need to run Fade_To() once and the pod will continue to fade even if you halt the program. In Timer3, there is a function called Fade_State(void) which will check to see if any of the RGB pods are currently in the process of fading from one color to the next. If a pod is fading, the routine will automatically update the pod one more fade step per interrupt until the pod has completely faded into the color that the user had specified. The fade rate can be calculated easily:

Fade Rate = The amount of fade steps needed to completely fade to another color
One Fade Step = 8ms per Interrupt
Fade Period = The amount of time that it takes for the pod to completely fade

Fade Rate = (Fade Period / One Fade Step)

Example #1
Lets fade RGB pod #8 to the default color PINK and set it to have a fade period of 400ms.

Fade Rate = (400ms / 8ms per interrupt) = 50

//Fade pod #8 to PINK over the course of 400ms
Fade_To(8, PODn[7], COLOR[PINK], 50);

The fade period will not be 100% accurate but that is fine as we do not need that precise of timing. With all of the other interrupts and code running it may deviate by a few microseconds each time, but that is fine for our application. If all of the pods are updating at the same time, the max time spent in Fade_State(void) is 1.3ms. We don't want to waste a large amount of time in any interrupt routine and this is pushing it, but it all still works fine. I originally had the pods grouped into clusters of five and each cluster was only updated every 4th interrupt cycle which brought the max time down to about ~400μs, which was better, but then I was only updating the fade steps of each pod every 4th Timer3 interrupt cycle instead of every single interrupt cycle. Either way it will work but by doing it every interrupt cycle it gives us more precision for the fade rates.

Step 53: Software: LED Rings and the Ball Washer Motors

The LED rings are controlled the same way as the RGB pods, only each LED ring only uses one output on a TLC5940 and not 3 outputs like each pod. There are three main functions that we use to control the LED rings and the ball washer motors, they are as follows:

void LED_no(UINT8 LED, UINT16 data)
The first parameter 'LED' will take an input value of 1 - 16 and determine which OUTx pin on the TLC5940 that we are controlling. The second parameter, 'data', will set the PWM value between 0 - 4095 for that output. This function is used to control the rail LED rings as well as the ball washer LED rings. To turn on LED ring #8 with a PWM value of 3230, we would write:

//Turn on LED ring #8 with a duty cycle of 3230
LED_no(8,3230);

void Fade_Ring(UINT8 LED, float OLD, float NEW, UINT16 delay)
This function uses the exact same format to fade the LED rings that Fade_To() uses to fade the RGB pods. It has has been modified to write to TLC5940 #5's outputs for the LED rings and ball washers but other than that it still uses the Fade_State() interrupt call to handle all of the fading once this function has been called. The global UINT16 array that is used to save the current value of each LED ring is called 'LEDring[16]'. Once the user calls this fade function, the interrupt routine will automatically update the LED ring one more fade step per interrupt until the ring has completely faded to the brightness level specified. The fade rate is calculated the same way that we calculated it for the RGB pods back in step #52. Here is an example of fading an LED ring to its max brightness over the course of 80ms:

//Begin fading LED Ring #3
Fade_Ring(3, LEDring[2], 4095, 10);

void Ball_Washer(UINT8 bw, UINT16 fan_speed, UINT16 pump_speed)
To control the ball washer motors, we will use the function called Ball_Washer(a,b,c) which will allow us to set the speeds for the fan and pump motors on one of the ball washers. The LED rings around the ball washer entry and exit holes are controlled in the same fashion as the LED rings on the rails, this function just uses the LED_no(LED,data) function to set the motor speeds. Here is how we would turn on the blower fan at half speed for ball washer #2:

//Turn on the blower fan at half speed for ball washer #2; Keep the pump turned off
Ball_Washer(2, 2048, 0);

Photo #1 contains the layout for each of the LED rings but I have also posted a video below that cycles the LED rings from #1 - #12 in that order.

Step 54: Software: How the IR Sensors Work

On this beer pong table, each RGB pod has one IR sensor and each ball washer uses two IR sensors to detect the entry and exit of a ping pong ball in the ABS pipe, giving us a total of 24 IR sensors. The value that comes from the IR sensors is an analog value, so we would need 24 analog input pins on the PIC24HJ128GP506A microcontroller if we were to connect up each sensor directly. Instead, we will use an 8-channel analog multiplexer to bring the 24 analog sensor lines down to 2 analog sensor lines to the microcontroller. We could actually use just 1 analog sensor line as only one 74HC4051 is active at any given time, but seeing as I had enough ADC resources on the PIC I decided to use one analog line for each RGB Pod Controller.

Each RGB Pod Controller contains two 74HC4051 8-channel multiplexers that give us a total of 16 input pins. If you look at the RGB Pod Controller schematic, you will see that each controller only uses 12 of those input pins for its side of the table (10 for the RGB pods and 2 for the ball washer). In other words, we use a total of four 74HC4051 multiplexers to control 24 inputs. Now I know what you're saying, "Why don't we just use three 74HC4051s to control all 24 lines? Isn't it 8 inputs per chip and a total of 24 inputs needed so (24 inputs / 3 chips = 8 inputs per chip)?". Well that would work great but the RGB Pod Controllers are two separate PCBs and I didn't want to share the 3rd 74HC4051 multiplexer amongst both. If we did share it, the PCBs would not be identical as one PCB would have two 74HC4051s on it and the other PCB would only have one, meaning a complete redesign of the PCBs. Seeing as these chips are about $0.60 a piece, I could justify using an extra chip to keep things simpler.

In order to read the IR sensor values, we need to poll through each 74HC4051 multiplexer and read the data off of each input pin that has an IR sensor connected to it. In the next step I will explain how to do just that.

Step 55: Software: Controlling the 74HC4051 Multiplexers

The 74HC4051 is fairly straightforward to operate, there is one common shared output that is labeled 'Z', eight input pins labeled 'Y0 - Y7', an enable pin and three digital select pins. The three digital select pins (A is LSb and C is MSb) are used to connect one Y input pin to the Z output pin, thus allowing us to take an ADC sample from pin Z. The enable pin must be pulled low to activate the 74HC4051 or else the pins will be in a disconnected high-impedance state.

I have attached a truth table for the 74HC4051 in photo #1. If we want to have the input from Y6 on the output of Z, we would set select pin C equal to 1, pin B equal to 1 and A equal to 0 (0b110 which equals decimal 6). We would then pull /E low to activate the chip and the input on Y6 would come out of pin Z and go into an analog pin on our microcontroller. It's as simple as that!

Now we just have to expand this concept to suit four 74HC4051 multiplexers. The only difference now is that we will have to control the enable pin on each of the 74HC4051s separately so that we only have one chip using its shared analog line at one time. Other than that, we still use the same method described above to set the Yx input to the Z output. The digital select lines are shared across all four 74HC4051s.

In photo #2 you will see that the analog output pins for each RGB Pod Controller (AN0 & AN1) are actually connected together on the Master PCB. Because we only have one multiplexer active at a time, we can actually share one analog line for all four 74HC4051s. If 3 of the 4 chips are off at any given time, then we know that only one chip is actually driving that analog input. I chose to keep the RGB Pod Controllers separated from each other and used two analog inputs on the microcontroller, one for each controller. You will notice that I have placed a voltage divider at AN0 and AN1 on the microcontroller, this protects the microcontroller by bringing the (max) 5V value down to the 3.3V range that the microcontroller is running at.

Max Vout on ANx = ((18kΩ / (10kΩ + 18kΩ)) * 5V) = 3.21V

I have attached six example photos which show how we can read the IR sensor values for the RGB pods and ball washers.

Step 56: Software: Reading the IR Ball Washer and Pod Sensors

Now that we know how to retrieve the analog values from the IR sensors, we will need to have a constant value to compare the real time readings against in order to determine if any objects are detected by the sensors. It is important that when we start up the beer pong table we don't have any cups or objects placed in the way of the IR sensors. At the very start of the program before the main loop, we run a function called 'Sensor_Calibration()' which will take a specified amount of samples from each IR sensor and average the values together and store the data in the global UINT16 array 'IR_cal[24]'. Since the program assumes that there were no objects in front of the sensors at calibration time, it will compare the real time readings of each IR sensor to its calibrated value and if the light intensity is quite a bit higher than the calibrated value, it will know that there is an object in front of the sensor.

The 'Sensor_Calibration()' routine takes less than one second to complete so the players can rack the beer cups right after they turn on the beer pong table. You will know if the beer pong table wasn't properly calibrated because it won't detect the cups over the RGB pods.

Once we have the calibrated values for the IR sensors, the program will continue into the main program loop where it will constantly update the analog readings from the IR sensors. The function 'Update_Sensors()' will store the new analog reading for each sensor in the global UINT16 array 'IR_value[24]' and then we will run one more function called 'Pack_Sensor_Data()' which will return one 32-bit variable that is used to represent the state of each sensor.

The last global variable that we use for the IR sensors is a 32-bit unsigned int called 'sensor_bits'. Since we are keeping track of two states for each sensor (object detected and object not detected), we can use one bit to represent one IR sensor. If you look at photo #2, I have created a chart that contains the bit layout for 'sensor_bits'. If an object has been detected, the pods corresponding bit will be set (1) and if no object is detected then the bit will be cleared (0). Since 'sensor_bits' is a global variable, we can access it within any function if we need to know the state of each sensor. Each function can mask off the data to find which pods detect cups and then execute its code accordingly.

Photo #3 shows the main routine and how these 3 functions are implemented in the code. We have not gone over the 'Pod_Detect()' function yet but I have included it in this example just to show how 'sensor_bits' is used.

Step 57: Software: How the Ball Washer Code Works

The ball washers are very cool features on this table and they are very simple to use. A player drops a ping pong ball into the left ball washer hole where an IR sensor will detect it and start a water pump, the water pump will shut off after two seconds and start the blower fan which will then blow the ping pong ball out of the exit hole and the player will grab it. Now that is all fine and dandy if the situation above happens 100% of the time, but what happens if the player doesn't ever grab the ping pong ball? Or what if the ball gets stuck? Or somebody accidentally trips the entry sensor with their fingers? Well then the ball washer would run indefinitely unless the player manually tripped the exit IR sensor. Seeing as I wasn't gonna rely on people manually tripping the exit IR sensor if things went awry, I decided to put some error handling in the ball washer code.

Note:
When the ball is blown out of the exit hole, the IR sensor will see the ball pass by but it will not see the ball when it is hovering over top of the exit hole. In order to make the ball washer function properly, we set the blower fan to run for three more seconds after the exit IR sensor sees the ball pass it, this way the player has enough time to grab the ping pong ball before the fan shuts off and it falls back down the pipe. If the player does not grab it, the ball washer will detect it falling back down the pipe and blow it back up. It will do this for a set amount of time before it times out and sets an error flag.

There are really only two situations where things will go wrong. The first situation is if a ball is put in the entry hole but it never comes out of the pipe to trip the exit sensor and finish the function. In this case, the ball washer would just keep running until somebody tripped the exit sensor manually or turned off the table. The second situation happens when the ping pong ball DOES come out of the exit hole and trip the sensor but the player doesn't grab the ball so the ball washer shuts off and the ball falls back down the pipe. If another ball is put into that same ball washer it may jam up as there will now be two balls in the pipe instead of one which is what it was designed for. In all of these cases, we don't want players to keep trying to use the ball washer and make the situation worse, instead we want to disable the ball washer until the issue has been fixed.

We will program the table so that if a ball gets stuck in the pipe or if a ball falls back down the pipe, the ball washer will be completely disabled until the issue has been resolved and the player 'resets' the ball washer. If a player tries to put a ball in the ball washer when it is disabled, the ball washer LED rings will blink on and off five times indicating that the ball washer is jammed. I have attached four flowcharts that explain each decision and process that the microcontroller performs in order to handle these errors. Here is a brief walk-through:

Situation #1
A ball is dropped into the entry hole on ball washer #2 but it gets stuck in the pipe and doesn't exit the ball washer. The ball washer times out and shuts off but the player tries to put another ball into the ball washer. The point where the player puts the second ball in is deonoted by **.

Ball_Washers_Detect(UINT8 detection)
1) Check to make sure that BW2_JAM is not set from the prior ball. It is cleared, so continue to the next step.
2) Set BW_ACTIVE = 1 so that any function that uses the ball washer LED rings for animations will not use them.
3) Turn on the entry LED ring and the ball washer water pump.
4) Allow two seconds to pass so that the pump has enough time to push the ball to the other end of the pipe.
5) Turn off the ball washer pump and turn on the blower fan at normal speed.
6) Check to see if the ball has passed the exit sensor yet.
7) It hasn't, let two seconds pass and keep monitoring the sensor to see if it passes.
8) It loops five times (about 10 seconds) but times-out as the ball did not come out of the exit hole.
9) Set BW2_JAM = 1 so that if another ball is detected at the entry hole of the ball washer the program knows that it is jammed.

**The player puts in the second ping pong ball into the jammed ball washer.
10) Ball_Washers_Detect(detection) detects the second ball and begins the function again.
11) The first thing that it checks for is if BW2_JAM is set and it is.
12) Set the global variable 'error_code' equal to BW2_JAM_ERROR_CODE.
13) Set DIAGNOSE_ERROR = 1 (The Timer3 interrupt routine will take care of the rest now that DIAGNOSE_ERROR is set).

Timer3()
11) Timer3 interrupts and checks to see if DIAGNOSE_ERROR == 1.
12) There is an error, call function Display_Error(error_code) and include the global variable which contains the error code.

Display_Error(error)
13) Check the error code to see what type of error needs to be displayed.
14) It is a BW2_JAM_ERROR_CODE, call function Ball_Washer_Jam_Error(error) to inform the player of the error.

Ball_Washer_Jam_Error(error)
15) Are ball washer #2's entry and exit sensors both blocked? (This is how a user 'resets' the ball washer).
16) They are not both blocked, set BW_ACTIVE equal to 1 so no other animations use the ball washer LED rings.
17) Blink the entry and exit rings of ball washer #2 on and off five times, this informs the player that there is a jam.
18) Clear DIAGNOSE_ERROR to 0 and clear BW_ACTIVE to 0.

Now that you have an idea of how the ball washer code is formatted, I have posted a video below for situation #2 where the ball is never grabbed from the exit hole. The blower fan speed will be reduced before it turns off so that if the ball hasn't been grabbed it will fall back down the pipe and trip the exit sensor again. The blower fan will then be sped up to its normal speed and blow the ball back out of the ball washer, it will repeat this up to three times and if the ball hasn't been grabbed by the third time it will shut off and set a ball washer jam error flag.

You can adjust the motor speeds for your ball washers in the miscellaneous.h header file under the defined constants BWx_PUMP_SPEED and BWx_FAN_SPEED ('x' being ball washer #1 or #2).

Step 58: Software: Grid Animations: Pong Animation

The default settings for the pong animation will set seven single pixel balls on the table in random locations and have them start traveling in random directions. After a short while, one ball will be removed from the grid and the other balls will increase their radius by one pixel, after another short while another ball will be removed and the other balls will increase their radius by one more pixel and it will continue this pattern until it gets down to one ball left that has a large radius. After the last ball bounces around the grid for a bit, it will explode outwards by calling the 'Circle_Out()' animation. If the pong animation is ran again right after it finishes, it looks quite cool because the single pixel balls are loaded on to the grid right after last ball has finished exploding from the previous run. The video below shows this effect.

Function Call
Pong_Game()

Code Location
Pong_Game.c
Pong_Game.h

Implementation
This is a general animation that must be placed inside the main part of the program. This will enable the function to update the animation sequences every time that the program loops through the main routine. This function will return a 0 when the animation has completed all of its sequences, otherwise it will return a 1 to indicate that the animation is still in progress.

Step 59: Software: Grid Animation: Sine Wave

This animation will display a sine wave across the LED grid on the beer pong table. The user can select between a regular sine wave where the pixels on the LED grid that light up are the wave itself and an inverted sine wave where every pixel on the grid is turned on except for the pixels in the wave itself. The size and frequency of the wave can be changed by going into the Draw_Sine(UINT8 state) routine and modifying the variables.

Function Call
Draw_Sine(UINT8 state)

Code Location
LED_Graphics.c
LED_Graphics.h

Implementation
This is a general animation that must be placed inside the main part of the program. This will enable the function to update the animation sequences every time that the program loops through the main routine. If the value of 'state' is equal to 1, the LED pixels that are ON will make up the sine wave. If the value of 'state' is equal to 0, the LED pixels that make up the sine wave will be off while the surrounding pixels are all turned on. The amplitude and speed of the wave can be adjusted in the function call by the variables 'amplitude' and 'delay. This function will return a 0 when the animation has completed one full sine wave cycle, otherwise it will return a 1 to indicate that the animation is still in progress.

Step 60: Software: Grid Animation: Dual Wave

This animation will draw two sine waves and start them at different locations. The waves then constantly intersect with one another and look like they are expanding and contracting into each other. When you view the animation from the side of the table as opposed to viewing it from the end of the table, the animation looks different.

Function Call
Dual_Wave(UINT8 state)

Code Location
LED_Graphics.c
LED_Graphics.h

Implementation
This is a general animation that must be placed inside the main part of the program. This will enable the function to update the animation sequences every time that the program loops through the main routine. If the value of 'state' is equal to 1, the LED pixels that are ON will make up the sine waves. If the value of 'state' is equal to 0, the LED pixels that make up the sine waves will be off while the surrounding pixels are all turned on. The amplitude and speed of the waves can be adjusted in the function call by the variables 'amplitude' and 'delay. This function will return a 0 when the animation has completed one full sine wave cycle, otherwise it will return a 1 to indicate that the animation is still in progress.


Step 61: Software: Grid Animation: Exploding Circle

This animation will simply increase the radius of a circle from a single pixel until all parts of the circle are outside of the four borders of the beer pong table which produces an "exploding" effect. I usually use small animations such as this to transition from one animation to the next, other than that they aren't too useful.

Function Call
Exploding_Circle()

Code Location
LED_Graphics.c
LED_Graphics.h

Implementation
This is a general animation that must be placed inside the main part of the program. This will enable the function to update the animation sequences every time that the program loops through the main routine. This function will return a 0 when the animation has completed all of its sequences, otherwise it will return a 1 to indicate that the animation is still in progress.

Step 62: Software: Grid Animation: Checkers

This animation will display a 32x12 checkerboard and switch between the regular and inverted view multiple times providing a flashy eye-catching animation. This animation was real easy to code as I just had to set the ON LED pixels to be staggered from the OFF LED pixels, provide a small delay, then invert the grid, provide another delay and do it all over again. Some of the neatest animations are the simplest ones to code!

Function Call
Checkers()

Code Location
LED_Graphics.c
LED_Graphics.h

Implementation
This is a general animation that must be placed inside the main part of the program. This will enable the function to update the animation sequences every time that the program loops through the main routine. This function will return a 0 when the animation has completed all of its sequences, otherwise it will return a 1 to indicate that the animation is still in progress. I have set this function to complete ten sequences for each of the two states before returning a 0 and completing the animation.

Step 63: Software: Grid Animation: Corner Circles

This animation is the same animation the we used on the first test of the 32x12 LED Grid array back in step #15! To create the animation, we first draw a single pixel circle in each corner of the LED grid, from there we expand the radius of each circle until every part of each circle is outside of the grid. Once the circles have full expanded, multiple new circles will be drawn in the center of the grid over a short time period and they will expand outwards until we eventually allow of the LED pixels on the grid to turn on.

Function Call
Corner_Circles()

Code Location
LED_Graphics.c
LED_Graphics.h

Implementation
This is a general animation that must be placed inside the main part of the program. This will enable the function to update the animation sequences every time that the program loops through the main routine. This function will return a 0 when the animation has completed all of its sequences, otherwise it will return a 1 to indicate that the animation is still in progress.

Step 64: Software: Grid Animation: Circle Out

This animation works the exact same way as the exploding circle animation except for it has a 1-pixel border drawn along the edges of the LED grid. This animation is used at the end of the pong animation when the ball explodes across the table. I had to create this animation instead of using Exploding_Circle() as I needed the border to stay around the outside of the LED grid as the circle exploded.

Function Call
Circle_Out()

Code Location
LED_Graphics.c
LED_Graphics.h

Implementation
This is a general animation that must be placed inside the main part of the program. This will enable the function to update the animation sequences every time that the program loops through the main routine. This function will return a 0 when the animation has completed all of its sequences, otherwise it will return a 1 to indicate that the animation is still in progress.

Step 65: Software: Grid Animation: Set Scrolling Text

This animation can scroll up to 63 characters (64 characters including the string terminator '\0') across the LED grid. This function is similar to the Fade_To() function in the sense that it only has to be called once, then an interrupt flag will be set and the Timer3 interrupt will take care of the rest of the animation. You will not be able to scroll any new text across the LED grid until the last Set_Scrolling_Text(*text*) function has completed and the proper flags have been cleared. The function will not execute if there is no characters in the string or the string exceeds 64 characters.

Function Call
Set_Scrolling_Text(char text[64])

Automatic Function Calls Used in the ISR
Update_Text()

Code Location
LED_Graphics.c
LED_Graphics.h

Implementation
This function only needs to be called once as the Timer3 interrupt routine will handle the rest of the scrolling text. Once you call this function, a flag is set and Timer3 will continue to update the scrolling text until all of the text has been displayed, then another flag will be set. You will not be able to scroll any more text until the SCROLL_FINISHED flag has been cleared by the user. As soon as this function is called, the passed in string is copied into a global char array which allows the Update_Text() function to access the string data and properly update the scrolling text.

Step 66: Software: Grid Animation: LED Scoreboard

The LED scoreboard is one of my favorite effects! In order for it to work properly, we must be updating the values of the IR sensors in the main loop so that we can figure out the score of the beer pong game. When we update the IR sensors, we store the values in a 32-bit global variable called 'sensor_bits'. We then pass that variable along to this function where we will draw a scoreboard on the table, tally the amount of cups on each side and display the score in the LED grid.

The first team to get the opposing team down to zero cups on the table will win. Each time one cup is removed from one side of the table, the score on that side of the table will be decreased by one. The scores on each side can be between ten and zero, in other words, each cup on the table is worth one point.

Function Call
LED_Scoreboard(UINT32 pod_sensors)

Secondary Function Calls
Update_Sensors()
Pack_Sensor_Data()

Code Location
LED_Graphics.c
LED_Graphics.h

Code Location - Secondary Functions
IR_Sensors.c
IR_Sensors.h

Implementation
This is a general animation that must be placed inside the main part of the program. This will enable the function to update the animation sequences every time that the program loops through the main routine. Along with this function, we also need to run Update_Sensors() and get the return value of Pack_Sensor_Data() in order to keep the scoreboard updating the score on each side of the table. The return value of Pack_Sensor_Data() is a 32-bit integer and gets passed into this function. This function then parses the packed data and determines the score of the beer pong game.

Combined Example
//Update the analog IR sensor values   
Update_Sensors();

//Determine if any objects are detected and pack the data into 'sensor_bits'
sensor_bits = Pack_Sensor_Data();

//Pass the packed sensor data into the animation and display the scoreboard
LED_Scoreboard(sensor_bits);

Step 67: Software: Grid Animation: Box in & Box Out

This animation will draw a single pixel border around the perimeter of the LED grid, wait a few milliseconds and then draw a 2-pixel border around the perimeter of the grid. It will continue to do this up to a 6-pixel border (a fully lit up grid) and then it will do the reverse of what it just did, resulting in a box in, box out animation. If you want to adjust the speed of it, just go into the function and modify the 'delay' variable.

Function Call
Box_Grid_In()

Code Location
LED_Graphics.c
LED_Graphics.h

Implementation
This is a general animation that must be placed inside the main part of the program. This will enable the function to update the animation sequences every time that the program loops through the main routine. This function will return a 0 when the animation has completed all of its sequences, otherwise it will return a 1 to indicate that the animation is still in progress.

Step 68: Software: Grid Animation: End Blast

This animation will shoot a line horizontally across the center of the LED grid to the other side of the table where it will then produce five 'exploding' circles followed by all of the LED rows lighting up behind it. To determine the direction that the animation will travel, one has to pass in a constant value of 1 for it to travel towards the master side and a constant value of 2 to travel towards the secondary side.

We want to be able to change the direction of the animation so that we can detect a cup removal off of a pod and then run this animation. Since there are cups on each side of the table, we need to be able to set which direction this animation travels. In this step I will only show you how the animation itself works and then later on I will show you how to combine it with the RGB pods to run this animation when a cup is removed. This function takes one parameter 'side' which will determine what side of the table the animation should travel towards. If you pass in the constant MASTER_SIDE (1), the animation will travel from the secondary side towards the master side and end there. The SECONDARY_SIDE (2) constant will travel from the master side towards the secondary side.

Function Call
End_Blast(UINT8 side)

Code Location
LED_Graphics.c
LED_Graphics.h

Implementation
This is a general animation that must be placed inside the main part of the program. This will enable the function to update the animation sequences every time that the program loops through the main routine. If the animation has finished or has been passed a value other than 1 or 2, it will simply return a 0. If it is in the process of completing an animation, it will return the same value as what had been passed into it (a 1 (MASTER_SIDE) or a 2 (SECONDARY_SIDE)). This allows us to keep track of the state of this animation in other functions.

Step 69: Software: Pod Animation: Fade Cups

The fade cups animation will light up each row of cups on each side of the table red, green, blue or white. Each row on the same side of the table will be one of those four colors but no two rows will be the same color. After a short delay, the higher colors in the pyramid will fade down into the next row while the very bottom row color will fade up into the top row of the pyramid. This is a nice relaxing animation. The fade rate and delay between each color fade can be adjusted in the function.

Function Call
Fade_Cups()

Code Location
Miscellaneous.c
Miscellaneous.h

Implementation
This is a general animation that must be placed inside the main part of the program. This will enable the function to update the animation sequences every time that the program loops through the main routine. This function will return a 0 when the animation has completed all of its sequences, otherwise it will return a 1 to indicate that the animation is still in progress.

Step 70: Software: Pod Animation: Ripple Out

This function will fade the center pod on each side of the table into one of the ten default colors, the outer pods that make up the triangle will then fade into the same color after a brief delay. It is supposed to look like the center pods color is bleeding out or 'rippling' into the outer colors. As soon as the outer pods start fading to the same color as the center pod, the center pod will start fading into a new color and this process will continue to repeat through all of the colors.

Function Call
Ripple_Out(UINT16 fade_rate, UINT16 delay)

Code Location
Miscellaneous.c
Miscellaneous.h

Implementation
This is a general animation that must be placed inside the main part of the program. This will enable the function to update the animation sequences every time that the program loops through the main routine. The first parameter 'fade_rate' will adjust the amount of time that it takes for the pods to fade from one color to the next. The second parameter adjusts the amount of delay between each color fade. This function will return a 0 when the animation has completed all of its sequences, otherwise it will return a 1 to indicate that the animation is still in progress.

Step 71: Software: Pod Animation: Pyramid Chase

This animation will start at the top of each RGB pod pyramid (pod #1 & pod #11) with one of the default ten colors and slowly fade the same color into the next pod, each pod mimics this until all of the pods have faded into the new color. The animation will then set the starting pods to a new color and do it all over again cycling through all of the default colors.

Function Call
Pyramid_Chase()

Code Location
Miscellaneous.c
Miscellaneous.h

Implementation
This is a general animation that must be placed inside the main part of the program. This will enable the function to update the animation sequences every time that the program loops through the main routine. This function will return a 0 when the animation has completed all of its sequences, otherwise it will return a 1 to indicate that the animation is still in progress.

Step 72: Software: Pod Animation: Pod Detect

This effect allows the user to take a cup off of the table (or vice versa) and have the RGB pod change to another color. It uses the global variable 'sensor_bits' which contains the detection status of each of the RGB pod. We have to make sure that we are continually updating these values in order for the function to work properly. You will see in my code that at the start of the main loop I update the sensor data and then continue on with the rest of the program.

Function Call
Pod_Detect(UINT32 detection, RGB off_color, RGB on_color)

Secondary Function Calls
Update_Sensors()
Pack_Sensor_Data()

Code Location
Miscellaneous.c
Miscellaneous.h

Code Location - Secondary Functions
IR_Sensors.c
IR_Sensors.h

Implementation
This is a general animation that must be placed inside the main part of the program in order to keep updating. This function does not use any interrupt delays, it will simply scan the RGB pods and set the pods color in accordance with the pods detection state. This function will not return any values.

Combined Example
//Update the analog IR sensor values  
Update_Sensors();

//Pack the sensor data
sensor_bits = Pack_Sensor_Data();

//Pass the packed sensor data into the animation and set the RGB pod colors
Pod_Detect(sensor_bits,COLOR[BLUE],COLOR[RED]);

Step 73: Software: Pod Animation: Color Throb

The color throb animation will allow the user to set the center pod of the pyramid to one color and the outer perimeter pods to a completely different color (or the same color if the user wishes). The center pod will just fade in and out continually while the outer pods are all dimmed except for one of those pods which will be at full brightness. The full brightness pod will then fade into the next pod in the pyramid and it will dim, the function then continues this pattern of having the outer pods chase each other and the center pod will look like it is 'throbbing".

Function Call
Color_Throb(RGB outside_color, RGB inside_color)

Code Location
Miscellaneous.c
Miscellaneous.h

Implementation
This is a general animation that must be placed inside the main part of the program. This will enable the function to update the animation sequences every time that the program loops through the main routine. This function will return a 0 when the animation has completed all of its sequences, otherwise it will return a 1 to indicate that the animation is still in progress.

Step 74: Software: Pod Animation: Cycle Colors

This animation will simply turn all of the RGB pods to the same color, wait for a brief period and then fade them into the next default color. This will fade through all of the default colors and is great for testing the RGB pods to ensure that all RGB pods are working properly.

Function Call
Cycle_Colors()

Code Location
Miscellaneous.c
Miscellaneous.h

Implementation
This is a general animation that must be placed inside the main part of the program. This will enable the function to update the animation sequences every time that the program loops through the main routine. This function will return a 0 when the animation has completed all of its sequences, otherwise it will return a 1 to indicate that the animation is still in progress.

Step 75: Software: Ring Animation: Cycle Rings

This animation will fade in the six LED ring rows one after the other until all of the LED rings are lit up. After each ring is turned on, the animation will do the reverse of what it just did and fade out each of the LED rings in the same order that if faded them in. The speed of the animation can be adjusted in the function by increasing or decreasing the 'delay' variable. If either of the ball washers are in use during this animation, the LED rings that pertain to the ball washers will not be modified until the ball washers

Function Call
Cycle_Rings()

Code Location
Miscellaneous.c
Miscellaneous.h

Implementation
This is a general animation that must be placed inside the main part of the program. This will enable the function to update the animation sequences every time that the program loops through the main routine. If BW_ACTIVE is set (due to a ball washer in use) the ball washer LED rings will not be affected by this animation. This function will return a 0 when the animation has completed all of its sequences, otherwise it will return a 1 to indicate that the animation is still in progress.

Step 76: Software: Ring Animation: Crossfade Rings

This function will fade two rings on the table at a time that are directly opposite from each other. It will start fading in ring #9 and ring #11, going to ring #10 and #12, over to ring #1 and ring #8, so on so forth. Once all of the rings have been lit, it will fade out the LED rings in the exact same order that they were faded in at. This animation does use the ball washer LED rings unless the BW_ACTIVE flag is set, then it will just skip over them. Enjoy the animation!

Function Call
Crossfade_Rings()

Code Location
Miscellaneous.c
Miscellaneous.h

Implementation
This is a general animation that must be placed inside the main part of the program. This will enable the function to update the animation sequences every time that the program loops through the main routine. This function will return a 0 when the animation has completed all of its sequences, otherwise it will return a 1 to indicate that the animation is still in progress.

Step 77: Software: Ring Animation: Ring Chase

This animation will light all of the LED rings dimly except for one which will be at 'max' brightness (brightness defined in the function). For every sequence the bright LED ring will dim and the next LED ring in the square will be lit up to full brightness. This will continue through all twelve LED rings in a box pattern around the table. The four ball washer LED rings will not be updated if BW_ACTIVE is set to 1 and the function will simply skip over them until that flag is cleared.

Function Call
Ring_Chase()

Code Location
Miscellaneous.c
Miscellaneous.h

Implementation
This is a general animation that must be placed inside the main part of the program. This will enable the function to update the animation sequences every time that the program loops through the main routine. This function will return a 0 when the animation has completed all of its sequences, otherwise it will return a 1 to indicate that the animation is still in progress.

Step 78: Software: Combined Animations: Animate on Detection

I have made this function to show as an example of how we can perform certain animations when a cup is picked up off of a pod (or placed on a pod). I execute the animation End_Blast(side) when a cup has been lifted off of one of the pods,  Pong_Game() is the animation that will be displayed when there is no detection change from the RGB pods IR sensors.

Whenever a player picks up a cup off of a pod, this function will detect it and activate the End_Blast(x) animation. Depending on which side the cup was picked up from, the animation will 'shoot' towards that side of the beer pong table, finish the animation and return to the Pong_Game() animation.

Function Call
Animate_On_Detection()

Code Location
Miscellaneous.c
Miscellaneous.h

Implementation
This is a general animation that must be placed inside the main part of the program. This will enable the function to update the animation sequences every time that the program loops through the main routine.

Step 79: Software: Combined Animations: Dual Wave on Detection

This function is the exact same as Animate_On_Detection() except for we are using the Dual_Wave animation instead of the Pong_Game() animation. There is one more element added to this combined animation too, for each cup removal the state of the Dual_Wave(x) animation will flip flop between a regular and inverted animation. The End_Blast(x) animation is activated whenever a cup is removed.

Function Call
Dual_Wave_On_Detection(void)

Code Location
Miscellaneous.c
Miscellaneous.h

Implementation
This is a general animation that must be placed inside the main part of the program. This will enable the function to update the animation sequences every time that the program loops through the main routine.

Step 80: Software: Combined Animations

You should have a pretty good understanding now of how to use the animations! I don't really like having the beer pong table do one animation the whole game, so I have made the following routines to cycle through various animations:

Cycle_Grid_Animations(void)
This will cycle through nine different LED grid animations. The amount of time spent on each animation is determined by the value of 'delay'. Alternatively, one can set 'delay' to be a really high value (so it never fully elapses) and then monitor the return value from the animation. Once the animation returns a value of 0, we know that the animation has finished and we could just move on to the next animation.

Cycle_Pod_Animations(void)
This is set up with the same format as Cycle_Grid_Animations(), only we control the RGB pod animations in this function. It is set to cycle through seven different pod animations.

Cycle_Ring_Animations(void)
This is set up exactly as the last two functions, only it controls the LED ring animations. This switches between three different animations.

The video below shows all three of these functions above working together. At the first part of this video, I am running a function called Startup_Animation(). As soon as the first sine wave begins, that is when the main program loop starts running and the combined animations take effect.

Step 81: Software: VU Meter Mode

On the Master PCB schematic you can locate the hardware portion of the VU meter around IC9 (LM386N-1). Photo #1 is a close up of the VU meter part of the schematic, all of the components there allow us to read the ambient sound intensity around the beer pong table. Instead of soldering an electret mic onto the Master PCB, I soldered a 2-pin wired connector to the electret mics positive and negative pads and then plugged an electret microphone into the connector. Either way works fine, just be sure to have an electret microphone connected up to the master PCB before trying to use the VU meter feature! I may have have forgot to do that at first. ;)

You will notice that there are three potentiometers within the amplifier circuit. Each of their purposes are listed below, as well as in photo #1.

VR1
This potentiometer will adjust the gain of the amplifier. If we have a higher resistance across the potentiometer, we will have less gain in the amplifier and vice versa. If we have too much gain we will pick up noise in the circuit but if we have too little gain we won't pick up much audio around the table. It may take a little bit of trial and error to figure out what resistance works best but it shouldn't be too critical. We can also filter out some of the noise in the software too by offsetting the received ADC value. A 1kΩ resistance works well here.

VR2
This will adjust the sensitivity of the microphone. I found that this doesn't make all that much of a difference, if you put in a 10kΩ fixed resistor instead of a potentiometer it will work fine.

VR3
This will adjust the damping of the signal. If we set the resistance real low, we will receive a lot of jittery ADC values. By setting this to a higher resistance, the DC converted audio signal feeding into the PIC will be much more stable. I find that keeping this from 5kΩ to 10kΩ works the best.

Operation
There are three main VU meter functions included in the source code and they all follow the same coding format. We read in the audio ADC value from analog channel AN2 with the Read_VU(void) function, we cap the ADC value at a maximum value of 31 which gives us 32 different audio intensities to work with (0 - 31). We can then program the beer pong table to modify any feature on the table at any of those 32 intensity levels. We can also increase or decrease the amount of intensity levels from the ADC value if we need to. Before we start calibrating the VU meter, take a multimeter and set the potentiometers near the values that I stated above. If VR3 is ever set to 0Ω, you sure won't be seeing an audio signal from the amplifier as it will be shorting to ground!

Calibrating the VU Meter
In order to get the desired operation from the VU meter, we will have to filter off any unwanted noise that is on the ADC line. Whether it is completely quiet in the room or extremely loud, there will always be a bit of voltage on the signal line that feeds into the ADC module. We don't want the beer pong table to interpret this voltage as audible noise so we must offset the ADC value.

If you go into the LED_Graphics.h file which contains the function prototypes for the VU meter functions, you will see two constants called VU_SENSITIVITY and VU_OFFSET. Follow the steps below to calibrate the VU meter:

1) Start up the MPLAB IDE and load up the source code. Go into the main function and comment out all of the current code.

2) Add the VU_Meter_Bar() function into the main loop (This should be the only code running in the main loop). Build the code and program the PIC on the Master PCB.

3) Make sure that the room is completely silent and look at the beer pong table. If any of the LED rows are on it is because the VU meter is picking up noise from the circuit.

4) If all of the LED rows were on while the room was completely silent, increase the VU_SENSITIVITY constant by 1. Rebuild the code and reprogram the PIC. Continue doing this until there are less than 32 LED rows lit up when it is silent in the room.

5) Now that there are less than 32 rows lit up on the LED grid, get a rough count of how many of the rows are still lit up. Go to the VU_OFFSET constant and set its value equal to the amount of rows that are lit up. Rebuild and reprogram the code.

6) It should be calibrated now and if you turn some music on or make noise you should see the VU meter respond very well to bass and low frequencies. If you still have a few rows that are lighting up when the room is silent, just increase the value of VU_OFFSET until the grid is completely cleared when it is silent and you'll be good to go!

If you can't get any response from the VU meter, make sure to check the resistance across VR1, VR2 and VR3 on the Master PCB and set them close to the values that I posted above. If you start up the VU_Meter_Bar() function and no LED rows are lit up when it is silent, you can keep decreasing the VU_SENSITIVITY and VU_OFFSET values until you see some noise, then backtrack one step until there is no more noise. This will ensure that the VU meter is at its optimal sensitivity. The three main VU meter animations are listed below.

VU_Meter_Bar(void)
This function will light up each row of the LED grid (32 rows) independently according to the sound intensity of the music. If the audio level is within values 0 and 26, the color of the RGB pods will be green. If the audio level is above 26 and below 31, the color of the RGB pods will be yellow. When the audio level maxes out at 31 the color of the RGB pods will be red and the LED rings on the table will also be turned on.

VU_Meter_Circle_Out(void)
This function will draw a circle in the middle of the LED grid and the radius of the circle is determined by the audio level from the ADC module. The radius of the circle expands as the audio level increases and is calculated as (audio level / 2). If the sound level is within values 0 and 26, the color of the RGB pods will be green. If the sound intensity is above 26 and below 31, the color of the RGB pods will be yellow. When the audio level maxes out at 31 the color of the RGB pods will be red and the LED rings on the table will also be turned on.

VU_Meter_Pod_Color(void)
This function will adjust the color of the RGB pods in relation to the audio intensity. If there is no sound, the RGB pods will be dimly lit as blue. As the audio intensity increases, the red channel will get brighter and overtake the dimly lit blue channel on the pods, giving off a reddish/pink color. When the audio level maxes out at 31 the LED rings on the table will also be turned on.

Remove all of the code from the main() while loop, type in one of these animations and program it to test it out! All three of these functions can be seen in the video below.

Step 82: Software: RF Mode: How It Works

As I have stated earlier in the project, the beer pong table does have wireless capabilities. In the next few steps, I will give you all of the info that you need in order to send and receive data from the table. I have not released the PC application source code yet as I have to comment the code and put it through more debugging, the PC side of things is not my specialty and it is all hacked together right now. The zip file below contains the source code for the beer pong table, the RF Master Board and the BPT Control Center application that is used to control the table from a PC. As for the USB code in the RF Master Board project, I have derived it from Jan Axelson's code. It is an extensive protocol to learn and I am not even close to fully understanding it, so the USB portion of the code may not be written as well as it should be.

I also provide all of the information in regards to packing, sending, receiving and interpreting the data. I had originally planned to leave the RF feature out of the Instructable as it adds another layer of complexity to it, but then I thought I might as well briefly explain how it works and then others will have the option to further develop it if they wish.

There are three main parts to the RF feature of table:

1) The PC application that is used to send data to the RF Master Board.

2) The RF Master Board which receives data and information from the PC application and then relays it to the RF Slave Board.

3) The RF Slave Board which is actually the Master PCB on the beer pong table. After receiving data from the RF Master Board, the slave board can interpret the data and make adjustments on the beer pong table according to the data that was received.

When the user clicks 'Update Data' on the PC application, the program will check to see which features need to be updated. It will then send the required data over the USB line to the RF Master PCB in packets of 64 bytes (65 bytes including the report ID, but we don't worry about that).

Once the RF Master Board receives the data from the PC, it will parse the data to find out which feature of the beer pong table that the PC wants updated. From there, the RF Master Board will separate the data into 32 byte packets and transmit the data wirelessly to the nRF24L01+ module on the beer pong table.

After receiving the parsed RF data, the beer pong table will check the command bytes to see which feature needs to be updated. Once it finds which feature to update, it will wait for the rest of the data if there is more to come, then it will use the newly received data and modify the specified feature on the beer pong table.

RF Master Board Component List

1x PIC18F4550 8-Bit Microcontroller

1x 20MHz Crystal

1x nRF24L01+ RF Module

1x USB Connector

1x 5-Pin Header

1x 2x4 Pin Header

1x 1N4001 Diode

1x 10kΩ Resistor

1x 120Ω Resistor

1x 5mm LED

1x 10μF Electrolytic Capacitor

2x 27pF Ceramic Capacitors

Step 83: Software: RF Mode: Data Packing

In this step. we are going to do a full walkthrough of how a packet of data from the PC gets transferred to the beer pong table. Make sure to use the flowcharts and command tables that I have provided in this step, they will probably help you more than the written text in this step.

Transferring data from the PC to the RF Master Board
All of the data that is transferred from the PC to the PIC18F4550 on the RF Master Board is transferred in 64 byte packets. Whether we need to send 3 bytes of data or 64 bytes of data, we always send a full 64 byte packet. The USB code actually sends 65 total bytes, but one byte is the Report ID and we don't use it.

Whenever we initiate a command and send data to the RF Master Board, the first two bytes of the first packet will be used to determine what feature we are going to updating on the table. This means that after the command bytes, we only have up to 62 bytes of raw data to use from that single packet. If we need to send more than 62 bytes of raw data, we are forced to send another USB packet after the first packet has been received. The only command that needs to do this is the Refresh Grid command.

Handling data from the PC and sending it to the beer pong table
The nRF24L01+ module has a maximum payload of 32 bytes. This means that for every 64 byte packet that we receive from the PC, we will need to send two 32 byte RF packets to transmit all of the data (that is if we need to use more than 32 bytes. If we use 32 bytes or less from the USB packet then we only need to send one 32 byte RF packet). The Update Rings, Activate RF Mode and Disable RF Mode commands are all contained in a 64 byte packet from the PC, but seeing as each of those commands use less than 33 bytes of data we can fit all of the data that we need into one 32 byte RF packet.

As for the other commands, we just transmit the first 32 byte RF packet, wait for an ACK from the RF Slave Board and then transmit the second 32 byte packet which contains the last 32 bytes of data from the received USB packet. That's it, the rest is handled by the beer pong table.

Receiving data from the RF Master Board
After receiving a packet from the RF Master Board, the beer pong table will separate the first two bytes in the packet into a 16-bit variable. This variable contains the command value which tells the beer pong table what feature to update. Once the beer pong table figures out which command has been sent, it will wait for more data to arrive if there is more coming, then it will parse the received data and update whichever feature was selected.

Note:
The values for the LED grid brightness and scroll speed are transmitted in the Update Rings command.

For the Refresh Grid and Update Rings command, the 16-bit values are packed into two separate bytes in little endian format. Each of their packed data charts show an example of how the data is packed.

Step 84: Software: RF Mode: Testing It Out

Now that we have a brief understanding of how the RF Mode on the beer pong table works, lets test it out! Connect up the RF Master Board to a laptop that is near the beer pong table and start up the BPT Control Center application. Make sure that the beer pong table is programmed, started up and ready to go.

Using RF Mode

1) Click the 'Connect Tx' button on the application. It should say "Device Connected" in the status box, otherwise it is not communicating properly with the Master RF Board.

2) Click the 'Activate RF Mode' button. Whatever animation is currently being displayed on the LED grid should pause.

3) Select which features that you would like to update. For examples sake, check 'LED Grid', 'RGB Pods' and 'LED Rings'.

4) Select the 'Auto Update' checkbox so that the features are being constantly updated.

5) Now if you right click, hold the button down and drag the cursor over top of the LED grid, it will start activating the LED pixels that the cursor goes over top of. As soon as the LED pixels activate on the application, they will activate on the table too.

6) Go up to the top right corner and select an RGB pod colour. Hit the 'Color All Pods' button. This will light up all of the RGB pods with the selected color.

7) Now select a different color and individually click on RGB pods. You will see them change 1 by 1 on the table.

8) Turn on a few of the LED rings and adjust the brightness level with the 'LED Rings' slider.

9) Test the LED grid brightness out by adjusting it with the middle slider.

10) Go over to the 'Features to Update' column and select the 'Scrolling Text' checkbox. The 'LED Grid' box should automatically uncheck itself if it was checked.

11) Type some text into the 'Set Scrolling Text' edit box. Text is limited to 62 characters. Once you have the text typed in, hit the 'Update Data' button, you will see the text start scrolling across the grid. Scrolling text will not update on its own, you have to manually hit the update data button whether you are using auto-update or not.

12) Adjust the 'Scroll Speed' slider to modify the speed of the scrolling text.

13) When you are done playing around with the RF Mode, click the 'Disable RF Mode' and the table will resume with the animations from where it left off in step #2.

There are a lot more features that I need to add to the application, such as being able to pick which animation is displayed on the beer pong table, a tournament mode with a leader board, user profiles, an online database, custom animations that can be sent from a PC to the table, etc. I just wanted to make a proof of concept to show users what can be done, and that's what this is. It's not real practical at the moment, but it is pretty neat. Enjoy the RF Mode video that I have posted below!


Step 85: Conclusion: Pit Falls

I had my fair share of setbacks with this project. From noise on the SPI line to a jittery VU meter, I have pretty much seen everything that can go wrong. The only way that I was able to complete this project was by working on known issues one at a time until I figured out what was going wrong. The table is very solid right now and works like a charm but that comes from hours and hours of development and testing.

Ultimately, the biggest pitfall of this project is making the PCBs. Especially the Master PCB as there is very little room for error around the 64-pin PIC. If there is enough interest in this project I will look into getting sets of PCBs made at a fab house and selling them for a reasonable price. If you are one of the users that want to create this project but will have a heck of a time making 24 PCBs, let me know and I will look around to get some boards professionally made.

Another pitfall that we must be aware of is the second ULN2803A transistor array that controls the four ball washer motors. Each output on this chip can supply a maximum of 500mA of current, we must be SURE that each fan motor and pump motor that we use draws less current than this. The motors for the fan blowers that I have sourced draw ~200mA at 12V and the pump motors draw ~230mA (eBay says 1A, but I tested them out before using them and this is not the case) at 12V. To stay on the safe side, we will still only operate one ball washer at a time and only one of the motors will be on at any given time. In the original circuit I was driving the motors through a MOSFET but I ran into some errors with that approach and opted to use low current motors instead.

One more issue with this table is that it is not very easy to transport. Aside from being large and bulky, the solder connections under the LED grid may come loose (this hasn't happened yet) if the table is subject to vibrations for an extended period of time (In the back of a truck while driving). A person could silicone each LED hole underneath the table to prevent this from happening, but if you would ever need to replace a LED on the grid it would be a tougher job. I may end up doing this in the future, I still haven't had any LEDs burn out so it's looking pretty rock solid.

Those are the three major pitfalls, I had little issues with the code here and there but that's expected with a project of this size. I didn't get too in-depth with source code for this beer pong table which was a bummer for me, but I have commented all of the code real well so it is a little easier to follow. If you ever have any questions feel free to PM me and I'll get back to you ASAP.

Step 86: Conclusion: What Would I Do Differently

Although I am very satisfied with this project, there is always room for improvement! The first thing that I would change on the Master PCB would be how I drive the LED rings and the ball washer motors. I hate how I run the TLC5940's active low output into an inverter which then travels into a ULN2803A driver chip. I had to add an inverter because I could not find an 8-channel PNP transistor array and I did not want to use 8 discreet PNP transistors. Looking back now though, the single transistor option would have looked much cleaner and saved me a few electronic components.

I would also move the nRF24L01+ module to the SPI1 module. Currently the RF module and the TLC5940 chips share the SPI2 bus while the SPI1 bus is not even being used. By doing this I wouldn't have to briefly disable the RGB pod XLAT timer interrupt every time that I wrote data to the the RF module.

The last thing that I would try to change on this project is the LED grid. I hate how a person has 768 solder connections to make for each LED and has to lay out the wire grid. If this wasn't just a one-off project I would seriously look into getting a 32x12 LED silicone mat made. Kind of like what the LED rings are made of. I imagine that this would be rather expensive, but a person can dream!

Step 87: Conclusion: References

Here are the links to the datasheets for the major components in the beer pong table circuit. There is plenty of information and projects available when searching Google for each of these components. If you are having trouble understanding a component, cross reference my code with the code of other projects to get a better understanding.

PIC24HJ128GP506A Datasheet
http://www.microchip.com.tw/Data_CD/Datasheet/16-Bits/70175F.pdf

HT1632C Datasheet
http://www.holtek.com/pdf/consumer/ht1632cv130.pdf

TLC5940 Datasheet
http://www.ti.com.cn/cn/lit/ds/symlink/tlc5940.pdf

nRF24L01+ Datasheet
http://www.nordicsemi.com/eng/content/download/2726/34069/file/nRF24L01P_Product_Specification_1_0.pdf

ULN2803A Datasheet
http://www.ti.com/lit/ds/symlink/uln2803a.pdf

LM386N Datasheet
http://www.ti.com/lit/ds/symlink/lm386.pdf

LM2576-3.3 Datasheet
http://www.ti.com/lit/ds/symlink/lm2576.pdf

Step 88: Conclusion: Project Review

Overall, I am very happy with this project. It was a huge task to design, develop, code and record all of the steps that were required to make this project a reality. I really didn't commit to this project at first, I just slowly picked away at it until I had got the the electronics working. Once I could see the table starting to come together, it was easy enough to keep on going and I started working on it more. The majority of this project was completed in the last 4 months from the time of writing. The Instructable write up took another few weeks as I would work on it here and there (it's tough to make time for these projects sometimes!) before really pushing to get it done.

I am very happy with the end result though and I have only got positive reviews from friends who have came and seen it/played on it. There is always ways to improve the hardware, the software and the physicial design of the table, but seeing as this was the first prototype, I can't complain! I love it.

I have entered this project into two contests, the Full Spectrum Laser Contest and the Sensors Contest. If you feel that this project deserves a vote in either one of those contests (or both) please vote for it!

Instructables asks that each user state what they would do with the prizes if they won a contest. Well, when I get started thinking about owning a laser cutter or a desktop 3D printer my mind starts racing! The major downfall of all of my projects is the fabrication process, it usually takes large sums of money and large orders to get enclosures or parts manufactured and it is also difficult to create intricate parts for a person with the average tool set. A laser cutter and a 3D printer would definitely remove that limitation! I would definitely be able to create more professional looking projects from scratch!

As for the sensors contest, there is a whole heap of prizes to be won! I could definitely put that oscilloscope to good use (my logic analyzer saved me on this project, it helped me see all the noise on the SPI bus from a bad power supply). The GoPro takes better photos and videos than what I currently use, so I could use that for documenting projects (as well as for it's intended use).

Anyways, I'm starting to go on a rant here! That's it for the project. We're done! If you ever need any help or have any questions, feel free to PM me or post a comment. Thanks for checking out my Interactive LED Beer Pong Instructable!

EDIT

This project won grand prize in the Full Spectrum Laser contest! Thank you to all who voted for me and I will be sure to put the laser cutter and 3D printer to good use!

Full Spectrum Laser Contest

Grand Prize in the
Full Spectrum Laser Contest

Sensors Contest

First Prize in the
Sensors Contest