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



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

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..