Some friends of mine love playing the arcade game called “Pop-a-Shot”. The object is to make as many baskets in a set amount of time as possible. You then get tickets or bragging rights based on your score. They asked if I could build one using the technologies I’ve been using for the Internet of Lego city. So I put together a protoype with LEGO to see what it would take to build a full scale version.

presenting…

Pop o Shop
poposhop

 

 

Overview

Build an electronic score keeper system using an Arduino microcontroller, some lights, sensors and Legos.

Game Play

Press start button to begin game.

Make as many baskets as possible in 30 seconds.

If 30 points have been scored, extend game time an additional 30 seconds.

A top score will be recorded and displayed and light show will activate.

Gallery

This slideshow requires JavaScript.

Build It

Supplies

  • Arduino (I used an Arduino Nano)

  • Ultrasonic Sensor – HC-SR04
  • 7 Segment Display x 2
  • RGB LED (pre-built with resistors)
  • LEGO bricks, jumper wires

Arduino

Download Source Code

Code Highlights

I won’t go into every line of the project code, but I did want to highlight some core functions of the system.

Code Flow

  • Wait in loop until start button is pressed. An ambient light cycle will run on the RGB while waiting.
  • Start button will set gameActive variable to true, which runs the game functions and begins the timer.
  • The ultrasonic sensor will read the distance within each loop iteration to detect a ball within 10 cm.
  • If a ball is present, the score is incremented, the LED changes to green and the display is updated.
  • If 30 points have been reached in this session, an additional 30 seconds will be added to the game time. It will repeat the cycle if an additional 30 points has been scored in the extended time.
  • A session counter will be updated and displayed.
  • When the game ends, the score will be compared against the existing high score. If it is exceeded, the top score will be updated and celebration function will run to update the lights and displays.

Main Setup

Initialize the displays, serial console and reset the game variables.

// Run ONCE
void setup() {
  Serial.begin(57600);
  pinMode(ledRedPin, OUTPUT);
  pinMode(ledGreenPin, OUTPUT);
  pinMode(ledBluePin, OUTPUT);
  pinMode(startButtonPin, INPUT);

  // LED 7 Segment logic

  lc.shutdown(0,false);
  lc2.shutdown(0,false);
  /* Set the brightness to a medium values */
  lc.setIntensity(0,8);
  lc2.setIntensity(0,8);
  /* and clear the display */
  lc.clearDisplay(0);
  lc2.clearDisplay(0);

  resetGame();
}

Main Loop

This is where we listen for the start button to be pressed and set the game status using the ‘gameActive’ variable.

// MAIN LOOP
void loop() {

  // Start Game Button
  if(digitalRead(startButtonPin)){
    Serial.print("Game Started");
    // reset score
    resetGame();

    // Clear last messages
    lc2.clearDisplay(0);
    // set game state
    gameActive = true;
  }

  // Run Game
  if (gameActive){
    runGame();

    if (statsMillis > 500) {    
      statsMillis = 0;         // reset the counter to 0 so the counting starts over...
      stats();
    }
  }else{
    standbyRoutine();
  }
}

Detecting Ball

  • Ultrasonic distance detection
#include <Ultrasonic.h>

void runGame(){
  // Determine remaining game time
  remaining = (countdown - sinceStart) / 1000;

  // Display Countdown Clock
  displays(btmLeftDisplay,remaining);
  

  // Check for ball, with delay to avoid duplicates
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= goalDelay) {  
    int distance =  ultrasonic.distanceRead();
    Serial.print("distance ");
    Serial.println(distance);

    ledRedState = HIGH;
    ledGreenState = LOW;
    ledBlueState = HIGH;
    digitalWrite(ledGreenPin, ledGreenState);
    digitalWrite(ledRedPin, ledRedState);
    digitalWrite(ledBluePin, ledBlueState);

    // Detect Ball
    if (distance < 10 && deDuplicate){
      deDuplicate = false;  
      // save the last time a goal happened
      previousMillis = currentMillis;
      Serial.print("Ball Detected!");
      score += 2;
      Serial.print("Score: ");
      Serial.println(score);
      ledGreenState = HIGH;
      ledRedState = LOW;
      ledBlueState = LOW;

      // set the LED with the ledState of the variable:
      digitalWrite(ledGreenPin, ledGreenState);
      digitalWrite(ledRedPin, ledRedState);
      digitalWrite(ledBluePin, ledBlueState);
    }else{
      deDuplicate = true;
    }

  // End game loop
  if(remaining < 1){
    // Check if bonus time is added
    if(score-lastScore >= bonus){
      bonusCount++;
      displays(btmRightDisplay,bonusCount);
      lastScore = score;   
      // add extended bonus time by resetting start counter
      sinceStart = 0;
    }else{
          // End Game
          if (score > topScore){
            topScore = score;
            finishTopScore();      
          } else{
            finishLowScore();     
          }
          resetGame(); 
          gameActive = false;                      
    }
  } 
  // write score to 7 Segment
  displays(topRightDisplay,score);  
  }

}

 

Seven Segment Displays

LEDControl Library

Setup the segment displays.

#include "LedControl.h"

// Define Hardware
/*
Seven Segment Display
 Pin 8-4 is connected to the DataIn 
 Pin 9-5 is connected to the CLK 
 Pin 7-6 is connected to LOAD 
 */
LedControl lc=LedControl(8,9,7,1);
LedControl lc2=LedControl(4,5,6,1);

// Segement selector (helper function used to create sections)
int topRightDisplay = 0;
int topLeftDisplay = 1;
int btmRightDisplay = 2;
int btmLeftDisplay = 3;

 

Printing Words

This brings me to the name of the game “Pop o Shop”. Why “Shop“? Because there was no “t” in the LedControl.h library! and I couldn’t be bothered with a hack for this prototype đŸ™‚

// Spell "P0P.Sh0P" btmRightDislay
    lc2.setChar(0,7,'P',false);
    lc2.setChar(0,6,'0',false);
    lc2.setChar(0,5,'P',false);
    lc2.setChar(0,4,false,true);
    lc2.setChar(0,3,'5',false);
    lc2.setChar(0,2,'h',false);
    lc2.setChar(0,1,0,false);
    lc2.setChar(0,0,'P',true);

 Printing Numbers

I have a function to leverage two 7-segment displays and format the numbers properly.

// Formats and prints to Seven Segment displays consitently
void displays(int section,int number){
  uint8_t ones,tens,hundreds,thousands;
  thousands=number/1000;
  hundreds=number%1000/100;
  tens=number%100/10;
  ones=number%10;
  // set digits on 7 segment. Some digits are disabled (' ') for aesthetic reasons
  if(section == 0){                   // Top Right
    lc.setDigit(0,0,ones,false);
    lc.setDigit(0,1,tens,false);
    lc.setDigit(0,2,hundreds,false);
    //lc.setDigit(0,3,thousands,false); 
    lc.setChar(0,3,' ',false);
  }else if(section == 1){             // Top Left
    lc.setChar(0,4,' ',false);
    lc.setDigit(0,5,ones,false);
    lc.setDigit(0,6,tens,false);
    lc.setDigit(0,7,hundreds,false);
    //lc.setDigit(0,7,thousands,false);  
  }else if(section == 2){             // Bottom Right
    lc2.setDigit(0,0,ones,false);
    lc2.setDigit(0,1,tens,false);
    //lc2.setDigit(0,2,hundreds,false);
    //lc2.setDigit(0,3,thousands,false);
    lc2.setChar(0,2,' ',false);
    lc2.setChar(0,3,' ',false);
  } else if(section == 3){             // Bottom Left
    lc2.setDigit(0,4,ones,false);
    lc2.setDigit(0,5,tens,false);
    //lc2.setDigit(0,6,false,false);
    //lc2.setDigit(0,7,false,false);
    lc2.setChar(0,6,' ',false);
    lc2.setChar(0,7,' ',false);
  }
  
}

 

RGB LED

First define the output pins.

This is a function that cycles through a range of numbers to change the colors of the lights when a new Top Score has been reached.

// RGB LED 
const int ledRedPin = 2;          // PIN 2
const int ledGreenPin = 3;          // PIN 3
const int ledBluePin = 11;         // PIN 11

// Top Score Celebration Routing
void finishTopScore(){
    Serial.print("NEW TOP SCORE !");
    displays(topLeftDisplay,topScore);
    int y = false;
    for (int x = 0; x < 1024; x = x + 20){
      for (int y = 100; y < 1024; y++){
        analogWrite(ledGreenPin, x);
        analogWrite(ledRedPin, y);
      }
       // Spell "P0P"
      lc2.setChar(0,3,' ',false);
      lc2.setChar(0,2,'P',false);
      lc2.setChar(0,1,0,false);
      lc2.setChar(0,0,'P',true);
      delay(50);
      lc2.clearDisplay(0);       
    }
}

 

Circuit

The circuit is pretty strait forward. Simply connect the Ultrasonic sensor, button and LED pins to the pin variables defined in the application. I made the button connection extendable so I could move the start button closer to the player if I built a proper basketball goal for this.

What’s in 2.0?!

This was just an idea at this stage. I actually play the game all the time so I think I will take it to the next level. I want to use IoT technologies to store the scores in the cloud. I also have a huge 32×64 LED matrix board and a doorbell that could add some great arcade effects.

Stay tuned..