Project showcase
Automated Beer Pong Game - Moving Cups!

Automated Beer Pong Game - Moving Cups! © GPL3+

Drinking game too easy? Here's a way to increase the difficulty to your liking!

  • 1,926 views
  • 0 comments
  • 1 respect

Components and supplies

Apps and online services

About this project

After seeing a few extremely difficult projects involving automating games, I thought I'd try to make an easier and unobtrusive version using Arduino and tools on hand!

Concept, Build, and Demonstration Video

The device uses a stepper motor, motor driver, limit switch, rotary encoder, and OLED display.

When turning on the cups run through a homing sequence to find the limits of the AL2020 rail it slides on. Afterwards, you are able to select your difficulty 1 - 10 and start the motor through the menu displayed on the OLED screen.

The table is made from a sheet of 3/4" plywood and some 2"x2" used as legs. It also uses 1/4" thick acrylic at the ends to show the mechanism below the table, while keeping the entire top of the table flush and flat. Thus, you can use the table as a normal beerpong table, or as the automated version if supplied with power!

Code

Code for Automated Beer Pong GameC/C++
This is for the game, some adjustments might have to be made if using a different motor driver (TB6600) or motor with different degrees of steps.
/*
 * Code by Ty Palowski for Automated Beerpong Game
 * https://youtube.com/c/typalowski
 */


#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <ClickEncoder.h>
#include <TimerOne.h>

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET     4 // Reset pin # (or -1 if sharing Arduino reset pin)

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// defines pins numbers
const int dirPin = 2; 
const int stepPin = 4; 
const int enPin = 5;

int menuitem = 1;
int frame = 1;
int page = 1;
int lastMenuItem = 1;

String menuItem1 = "Motor: OFF";
String menuItem2 = "Difficulty";
String menuItem3 = "Mode";
String menuItem4 = "Reset";

boolean motor = true;
int stepdelay;

int maxDelay = 240;
int diffDelta = 22;

String difficulty[10] = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" };
int selectedDifficulty = 0;

String mode[2] = { "EASY", "HARD"};
int selectedMode = 1;

boolean up = false;
boolean down = false;
boolean middle = false;

ClickEncoder *encoder;
int16_t last, value;

// '85062033-beer-pong-or-beirut-drinking-game-with-cups-with-ball-line-art-vector-icon-for-apps-and-websites-', 128x32px
const unsigned char myBitmap [] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x7c, 0x7c, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x83, 0x83, 0x83, 0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x98, 0x38, 0x38, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9f, 0xff, 0xff, 0xf3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0xc3, 0xc3, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0x83, 0x83, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0xff, 0xff, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0x98, 0x33, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x9c, 0x71, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x9f, 0xf1, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x90, 0x13, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x98, 0x13, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xc8, 0x23, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

#define home_switch 9 // Pin 9 connected to Home Switch (MicroSwitch)

int direction;    // Variable to set Rotation (CW-CCW) of the motor
int steps;        // Used to set HOME position after Homing is completed

int fullDist = 32766;
int halfDist = fullDist/2;


void setup() {
  Serial.begin(9600);

  
  // Sets the two pins as Outputs
  pinMode(stepPin,OUTPUT); 
  pinMode(dirPin,OUTPUT);

  pinMode(enPin,OUTPUT);
  digitalWrite(enPin,LOW);


  pinMode(7,OUTPUT);

  
  encoder = new ClickEncoder(A1, A0, A2);
  encoder->setAccelerationEnabled(false);

  display.begin();      
  display.clearDisplay(); 

  Timer1.initialize(1000);
  Timer1.attachInterrupt(timerIsr); 
  
  last = encoder->getValue();

  display.setRotation (2);

  display.drawBitmap(0, 0, myBitmap, 128, 64, WHITE); // display.drawBitmap(x position, y position, bitmap data, bitmap width, bitmap height, color)

  // Show the display buffer on the screen. 
  display.display();


// Start Homing procedure of Stepper Motor at startup
Serial.println("Homing...");
  while (digitalRead(home_switch)) {  // Do this until the switch is activated   
    digitalWrite(dirPin, HIGH);      // (HIGH = anti-clockwise / LOW = clockwise)
    digitalWrite(stepPin, HIGH);
    delayMicroseconds(75);                       // Delay to slow down speed of Stepper
    digitalWrite(stepPin, LOW);
    delayMicroseconds(75);   
}

  while (!digitalRead(home_switch)) { // Do this until the switch is not activated
    digitalWrite(dirPin, LOW); 
    digitalWrite(stepPin, HIGH);
    delay(5);                       // More delay to slow even more while moving away from switch
    digitalWrite(stepPin, LOW);
    delay(5);
  }

  steps=0;  // Reset position variable to zero

    digitalWrite(dirPin,LOW); // Enables the motor to move in a particular direction
  // Makes 200 pulses for making one full cycle rotation
  for(int x = 0; x < halfDist+400; x++) {
    digitalWrite(stepPin,HIGH); 
    delayMicroseconds(50); 
    digitalWrite(stepPin,LOW); 
    delayMicroseconds(50); 
    }
   Serial.println("Ready!");
  delay(1000);

}

void loop() {

stepdelay = maxDelay - selectedDifficulty*diffDelta;

      if (motor == true) 
      {
        turnMotorOff();
        }
      else
      { 
        turnMotorOn();
       }

  drawMenu();

  readRotaryEncoder();

   ClickEncoder::Button b = encoder->getButton();
   if (b != ClickEncoder::Open) {
   switch (b) {
      case ClickEncoder::Clicked:
         middle=true;
        break;
    }
  }    
  
  if (up && page == 1 ) //We have turned the Rotary Encoder CounterClockwise
  {
    up = false;
    if(menuitem==1 && frame ==2)
    {
      frame--;
    }
     if(menuitem==2 && frame ==3)
    {
      frame--;
    }
         if(menuitem==3 && frame ==4)
    {
      frame--;
    }
    
    lastMenuItem = menuitem;
    menuitem--;
    if (menuitem==0)
    {
      menuitem=1;
    }
  } 
  else if (up && page == 2 && menuitem==2 ) 
  {
    up = false;
    selectedDifficulty--;
    if(selectedDifficulty == -1)
    {
      selectedDifficulty = 9;
    }
  }
  else if (up && page == 2 && menuitem==4 ) {
    up = false;
  }
   else if (up && page == 2 && menuitem==3 ) {
    up = false;
    selectedMode--;
    if(selectedMode == -1)
    {
      selectedMode = 1;
    }
  }
  
  if (down && page == 1) //We have turned the Rotary Encoder Clockwise
  {

    down = false;
    if(menuitem==2 && lastMenuItem == 1)
    {
      frame ++;
    }
    else  if(menuitem==3 && lastMenuItem == 2)
    {
      frame ++;
    }
    else  if(menuitem==4 && lastMenuItem == 3 && frame!=3)
    {
      frame ++;
    }
    lastMenuItem = menuitem;
    menuitem++;  
    if (menuitem==5) 
    {
      menuitem--;
    }
  }
   else if (down && page == 2 && menuitem==2 ) {
    down = false;
    selectedDifficulty++;
    if(selectedDifficulty == 10)
    {
      selectedDifficulty = 0;
    }
  }
  else if (down && page == 2 && menuitem==3 ) {
    down = false;
    selectedMode++;
    if(selectedMode == 2)
    {
      selectedMode = 0;
    }
  }
  
  if (middle) //Middle Button is Pressed
  {
    middle = false;
   
    if (page == 1 && menuitem==1) // Motor Control 
    {
      if (motor) 
      {
        menuItem1 = "Motor: ON";
        motor = false;
        turnMotorOff();
        //Left to Center
    digitalWrite(dirPin,LOW); // Enables the motor to move in a particular direction
  for(int x = 0; x < halfDist; x++) {
    digitalWrite(stepPin,HIGH); 
    delayMicroseconds(stepdelay); 
    digitalWrite(stepPin,LOW); 
    delayMicroseconds(stepdelay); 
    }
        }
      else 
      { 
        menuItem1 = "Motor: OFF";
        motor = true;
        turnMotorOn();
        //Center to Right
    digitalWrite(dirPin,HIGH); // Enables the motor to move in a particular direction
  for(int x = 0; x < halfDist; x++) {
    digitalWrite(stepPin,HIGH); 
    delayMicroseconds(stepdelay); 
    digitalWrite(stepPin,LOW); 
    delayMicroseconds(stepdelay); 
    }
       }
    }

    if(page == 1 && menuitem ==4)// Reset
    {
      resetDefaults();
    }
    else if (page == 1 && menuitem==2) {
      page=2;
     }
    else if (page == 1 && menuitem==3) {
      page=2;
     }
    else if (page == 2) 
     {
      page=1; 
     }
   }   
  }
  
  void drawMenu()
  {
    
  if (page==1) 
  {    
    display.setTextSize(1);
    display.clearDisplay();
    display.setTextColor(WHITE, BLACK);
    display.setCursor(15, 0);
    display.print("MAIN MENU");
    display.drawFastHLine(0,10,128,WHITE);

    if(menuitem==1 && frame ==1)
    {   
      displayMenuItem(menuItem1, 15,true); //FRAME 1
      displayMenuItem(menuItem2, 25,false);
    }
    else if(menuitem == 2 && frame == 1)
    {
      displayMenuItem(menuItem1, 15,false);
      displayMenuItem(menuItem2, 25,true);
    }
    else if(menuitem == 3 && frame == 2)
    {
      displayMenuItem(menuItem2, 15,false); //FRAME 2
      displayMenuItem(menuItem3, 25,true);
    }

      else if(menuitem == 2 && frame == 2)
    {
      displayMenuItem(menuItem2, 15,true);
      displayMenuItem(menuItem3, 25,false);
    }
    else if(menuitem == 4 && frame == 3)
    {
      displayMenuItem(menuItem3, 15,false); //FRAME 3
      displayMenuItem(menuItem4, 25,true);
    }
    
    else if(menuitem == 3 && frame == 3)
    {
      displayMenuItem(menuItem3, 15,true);
      displayMenuItem(menuItem4, 25,false);
    }
    else if(menuitem == 2 && frame == 3)
    {
      displayMenuItem(menuItem2, 15,true);  //FRAME 2
      displayMenuItem(menuItem3, 25,false);
    }   
    else if(menuitem == 1 && frame == 2)
    {
      displayMenuItem(menuItem1, 15,true);  //FRAME 1
      displayMenuItem(menuItem2, 25,false);
    }   
    display.display();
  }
  else if (page==2 && menuitem == 2) 
  {
   displayStringMenuPage(menuItem2, difficulty[selectedDifficulty]);
  }
   else if (page==2 && menuitem == 3) 
  {
   displayStringMenuPage(menuItem3, mode[selectedMode]);
  }  
  }


  void resetDefaults()
  {
    selectedDifficulty = 0;
    selectedMode = 0;
    motor = true;
    menuItem1 = "Motor: OFF";// Start Homing procedure of Stepper Motor at startup

  while (digitalRead(home_switch)) {  // Do this until the switch is activated   
    digitalWrite(dirPin, HIGH);      // (HIGH = anti-clockwise / LOW = clockwise)
    digitalWrite(stepPin, HIGH);
    delayMicroseconds(75);                      // Delay to slow down speed of Stepper
    digitalWrite(stepPin, LOW);
    delayMicroseconds(75);   
}

  while (!digitalRead(home_switch)) { // Do this until the switch is not activated
    digitalWrite(dirPin, LOW); 
    digitalWrite(stepPin, HIGH);
    delay(5);                       // More delay to slow even more while moving away from switch
    digitalWrite(stepPin, LOW);
    delay(5);
  }

  steps=0;  // Reset position variable to zero

    digitalWrite(dirPin,LOW); // Enables the motor to move in a particular direction
  for(int x = 0; x < halfDist+400; x++) {
    digitalWrite(stepPin,HIGH); 
    delayMicroseconds(50); 
    digitalWrite(stepPin,LOW); 
    delayMicroseconds(50); 
    }
   }

  void setstepdelay()
  {
  //  display.setContrast(contrast);
    display.display();
  }

  void turnMotorOn()
  {
//Left limit to Right Limit
    digitalWrite(dirPin,HIGH); 
  for(int x = 0; x < fullDist; x++) {
    digitalWrite(stepPin,HIGH); 
    delayMicroseconds(stepdelay); 
    digitalWrite(stepPin,LOW); 
    delayMicroseconds(stepdelay); 
    }
//Right limit to Left
    digitalWrite(dirPin,LOW); 
  for(int x = 0; x < fullDist; x++) {
    digitalWrite(stepPin,HIGH); 
    delayMicroseconds(stepdelay); 
    digitalWrite(stepPin,LOW); 
    delayMicroseconds(stepdelay); 
    }
  }

    void turnMotorOff()
  {
    digitalWrite(7,HIGH);
  }

  void timerIsr() {
  encoder->service();
}

void displayIntMenuPage(String menuItem, int value)
{
    display.setTextSize(1);
    display.clearDisplay();
    display.setTextColor(WHITE, BLACK);
    display.setCursor(10, 0);
    display.print(menuItem);
    display.drawFastHLine(0,10,80,WHITE);
    display.setCursor(5, 15);
    display.print("Value");
    display.setTextSize(2);
    display.setCursor(80, 10);
    display.print(value);
    display.setTextSize(2);
    display.display();
}

void displayStringMenuPage(String menuItem, String value)
{
    display.setTextSize(2);
    display.clearDisplay();
    display.setTextColor(WHITE, BLACK);
    display.setCursor(0, 0);
    display.print(menuItem);
    display.setCursor(50, 15);
    display.print(value);
    display.setTextSize(2);
    display.display();
}

void displayMenuItem(String item, int position, boolean selected)
{
    if(selected)
    {
      display.setTextColor(BLACK, WHITE);
    }else
    {
      display.setTextColor(WHITE, BLACK);
    }
    display.setCursor(0, position);
    display.print(">"+item);
}

void readRotaryEncoder()
{
  value += encoder->getValue();
  
  if (value/2 > last) {
    last = value/2;
    down = true;
    delay(150);
  }else   if (value/2 < last) {
    last = value/2;
    up = true;
    delay(150);
  }
}

Custom parts and enclosures

Magnet Shuttle
Magnet shuttle which fits ontop of AL202 Slider
Stepper Motor Holder
Holds stepper motor and AL2020 rail.
Limit Switch Holder
Holds limit switch and AL2020 Rail

Schematics

Wiring Schedule for Components
Works with Arduino Uno and Nano. Needs 12V supply and 5V to power motor driver and Arduino, respectively.
Wiring diagram vcxfjwfmmx

Comments

Similar projects you might like

Completely Automated M&M Launcher - Activated Using Alexa

Project showcase by HarrisonMcIntyre

  • 1,545 views
  • 0 comments
  • 17 respects

Mind Control Beer Pong!

Project showcase by typalowski

  • 2,136 views
  • 1 comment
  • 3 respects

Automated Password Typer

Project showcase by mPelectronic

  • 20,369 views
  • 30 comments
  • 47 respects

Arduino Pong Game on 24x16 Matrix with MAX7219

Project tutorial by Mirko Pavleski

  • 6,515 views
  • 6 comments
  • 23 respects

Hookah Lighting

Project tutorial by Turai Botond

  • 4,457 views
  • 0 comments
  • 6 respects

Automated Light Switch

Project tutorial by Hung-Che

  • 8,167 views
  • 3 comments
  • 17 respects
Add projectSign up / Login