Project tutorial
Animated Ping Pong Scoreboard (updated with videos)

Animated Ping Pong Scoreboard (updated with videos)

An animated scoreboard with sound that keeps track of score, games won, who is serving and entertains with animation and digitized voices.

  • 1,299 views
  • 0 comments
  • 6 respects

Components and supplies

About this project

Ping Pong Scoreboard Startup

Announcing Change of Serve

End of Game Sequence congratulations, cheers, etc.

We play a lot of ping pong but seem to constantly forget the score, whose serve it is, etc. So I created a ping pong scoreboard that keeps track of all that and has built in entertainment. Two big buttons on the top are tapped for each point a player or team scores. There's a startup animation and sound loop with a bouncing ball, paddles and table that uses the RGB 16x32 matrix. The matrix also works like a billboard for service, new game, who won, etc. with active sound from an Adafruit FX Soundboard (cheering, naming who won, telling to switch serve, new game and game over, etc.). The big LED matrix keeps the game score and the smaller one keeps the match score (games won). There's a small LCD at the bottom that shows the settings that can be changed including length of game (11 vs. 21) and length of serve (we play 5 but official is 2). There's a reset button, buttons for undoing mistaken or disputed points, and one for manually switching the serve.

Code

Ping Pong Scoreboard CodeArduino
The ping pong scoreboard sketch
 


/* Ping Pong Scorekeeper
 Device to take care of scoring, switch of serve, keeping track of games won, etc.
  Configurable for different rule changes (end of game, switching serves, etc.)
  Includes sound effects
 */


#include <Wire.h>
#include <RGBmatrixPanel.h>
#include <LiquidCrystal_I2C.h>
#include <Adafruit_GFX.h>
#include "Adafruit_LEDBackpack.h"




Adafruit_7segment matrix = Adafruit_7segment();                   // Score display
Adafruit_7segment matrix1 = Adafruit_7segment();                  // Games/match display
LiquidCrystal_I2C lcd(0x27,16,2);                                 // initialize LCD


int DEBOUNCE =250;                // button debouncer
int which_button;                 // return from button query
const int ScoreToSwitch = A8;    // the number of pin for switch for  "11 or 21" as end of game
const int ServeToSwitch = A9;     // number of pin for switch for "Serve 2 or Serve 5" before switching
const int JISwitch = A10;         // Switches between Joan and Irwin vs. Player 1 and Player 2
const int firstbuttonpin = 2;      // Use pins 2-7  for reading the scoring buttons
const int numbuttons = 6;           //number of buttons to check
int lastreading[numbuttons];        // for button read routine
const int scrolldelay = 500;        // time to pause between scrolled text items
const int delaygamenumber = 2000;   // milliseconds to let "Game #xx" linger

// Most of the signal pins for the 16 x 32 RGB matrix are configurable, but the CLK pin has some
// special constraints.  On 8-bit AVR boards it must be on PORTB...
// Pin 8 works on the Arduino Uno & compatibles (e.g. Adafruit Metro),
// Pin 11 works on the Arduino Mega.  On 32-bit SAMD boards it must be
// on the same PORT as the RGB data pins (D2-D7)...
// Pin 8 works on the Adafruit Metro M0 or Arduino Zero,
// Pin A4 works on the Adafruit Metro M4 (if using the Adafruit RGB
// Matrix Shield, cut trace between CLK pads and run a wire to A4).

//#define CLK  8   // USE THIS ON ARDUINO UNO, ADAFRUIT METRO M0, etc.
//#define CLK A4 // USE THIS ON METRO M4 (not M0)
#define CLK 11 // USE THIS ON ARDUINO MEGA
#define OE   9
#define LAT 10
#define A   A0
#define B   A1
#define C   A2

int   Red =     0xF800;                        // Color definitions for RGB panel
int   Black =   0x0000;
int   Yellow =  0xFFE0;
int   Green =   0x07E0;
int   White =   0xFFF;
int   Blue =    0x001F;
int   Cyan =    0x07E0;
int   Magenta = 0xF81F;


// Define RGB matrix width and height.
const int mw = 32;                     //matrix width in pixels
const int mh = 16;                     // total matrix height in pixels
const int cw = 6;                       // width of a character (size 1) in pixels
const int Line1 = 0;                    // first line of RGB panel (just easier to remember)
const int Line2 = 1;                    // second line
const int scrolldelayc = 100;            // time between successive characters in scroll
const int flashdelay = 1000;              // delay for flash display
const int afterscrolldelay = 2000;        // when finished with scroll, leave it up a bit

const int bouncedelay = 45;
const int bouncesize = 25;
const int firstx = 3;                   // x coordinate for first and last ping pong ball position
const int lastx = 28;                   // for start up graphic routine
const int bouncey[bouncesize] = {1,3,5,7,9,11,13,11,9,7,5,3,1,3,5,7,9,11,13,11,9,7,5,3,1}; // y coordinates for each x ball
const int ballsize = 1;                 // 2 looks nicer but gives a GIANT ball!


RGBmatrixPanel matrixp(A, B, C, CLK, LAT, OE, false);       // initialize 16 x 32 matrix


/*
 * Definition of buttons
 *    1   Reset Match and Game 
 *    2   Force switch of service (but don't reset game)
 *    3   Score point for Player 1
 *    4   Unscore point for Player 1 (it was a mistake or disputed,etc.)
 *    5   Score point for Player 2
 *    6   Unscore point for Player 2

 */

int Score[2];                          //Score
int Games[2];                         // Games won
bool Player1isServing;                // flag for which play has serve
int ScoreTo = 21;                     // game length will be 11 or 21
int ServeTo = 5;                      // serve length will be 2 or 5
bool JI = false;                      // assume generic players
int switchread;
int i;
const int Player1 = 0;                // Position in array for first player/team
const int Player2 = 1;                // Same for "other" player

// Voice/sound files
/*
 The sounds are all on the FX Sound board in the form of sequential WAV files
 that are name T0 through T6 (board has a maximum of 0-10 (11) files in this form (GPIO)
 */

const int SoundStartup = 0;                   // Sound to play at startup Ping Pong ball then Shall We Play a Game
const int SoundChangeServe = 1;                // Simple bell or tone
const int SoundNewGame = 2;                     // "New Game"
const int SoundGameOver = 3;                    //  Game Over + Cheering  
const int SoundWinnerIs1 = 4;                   //   And the Winner is Player 1
const int SoundWinnerIs2 = 5;                   //   And the Winner is Player 2
const int SoundUndo = 6;                        // Point was a mistake
const int SoundWinnerIsJoan = 7;                  // And the winner is Joan
const int SoundWinnerIsIrwin = 8;                 // same for Irwin
const int SoundLetsPlay = 9;                      // Lets Play Ping Pong

       

const int FirstSoundPin = 36;             // first pin to use for soundboard (0-10 available on soundboard);
const int SizeSoundPin = 10;               // number of pins used (36-45)
const int fxset = 150;                     // Time soundboard must be held LOW to register (documentation says 125 ms)
const int SoundBusyPin = 8;                 // Soundboard pin that goes LOW when board is active
const int SoundBusyDelay = 25;                  // Period to wait between checking to see if sound has finished
int SoundBusyValue;
bool SoundBusy;                               // set true when busy


void setup() {
 
    which_button = 0;
    for (i = firstbuttonpin; i < firstbuttonpin+numbuttons; i++) {
    pinMode(i, INPUT_PULLUP);
    lastreading[i] = HIGH;}                             // initialize all buttons 'off'
    pinMode(ScoreToSwitch, INPUT_PULLUP);             // These three are toggle switches that change
    pinMode (ServeToSwitch, INPUT_PULLUP);             // the rules of the game (Win Score [11 or 21] and Serve change frequency [2 or 5])
    pinMode (JISwitch, INPUT_PULLUP);             // and "Joan and Irwin" vs. "Player 1 and Player"
    pinMode(SoundBusyPin, INPUT_PULLUP);
    
    for (i=FirstSoundPin; i <FirstSoundPin+SizeSoundPin; i++){           // pins for sound board
      pinMode(i,OUTPUT);            // each an output
      digitalWrite(i, HIGH);  }     // and initialize high (off)         
    Player1isServing = true;                      // default, can be switched with button 2 (switch serve)
    for (i=0; i <2; i++) {                        // clear score and matches
      Score[i] = 0;
      Games[i] = 0;
    }
    Serial.begin (9600);
    Wire.begin();                     // initialize I2C interface
    matrix.begin(0x70);              // initialize Score LED
    matrix1.begin(0x71);              // and Game LED
    matrixp.begin();                    // initialize 16x32 RGB panel
    matrixp.fillScreen(Black);          // and clear it
    matrix1.setBrightness(10);         // mid level brightness for Game display (0-15)
    lcd.init();                       // initialize LCD
    lcd.backlight();    

    DisplaySetandServe();             // Initial Displays for LCD
    BeginMatch();                     // and begin the match with "Game #"

   
} // End of Setup

void loop() {
  // put your main code here, to run repeatedly:

     DisplaySetandServe();                                    // Check the settings of "Play to" and "Each Service"

which_button = check_switches();
    if (which_button != 0) {
  
 switch (which_button) {
    case 0:    // no button pushed
      break;
    
    case 1:    // Set Zero to Match and Game Score 
      for (i=0; i <2; i++) {
      Score[i] = 0;
      Games[i] = 0;}
      BeginMatch();
      break;
    
    case 2:    // Just switch serve and don't reset game
       Player1isServing = !Player1isServing;
       PlaySound(SoundChangeServe);
       DisplayServe(Player1isServing);
       break;
       
    case 3:    // Point for Player 1
      ScoreUp(Player1);
      break;
      
    case 4:    // That was a mistake so 'unscore'
      if (Score[Player1] > 0) {Score[Player1]--;}
      PlaySound(SoundUndo);
      DisplayScore();
      break;
      
    case 5:    // Point for Player 2
      ScoreUp(Player2);
      break;
      
    case 6:    // That was a mistake so 'unscore'
      if (Score[Player2] > 0) {Score[Player2]--;}
      PlaySound(SoundUndo);
      DisplayScore();
      break;
      
   default:
      Serial.println("Should never get here!");
      break;
   
}    //  End of Switch
    }  // end of "if non-zero button"
    
}          // end of Void Loop

void ScoreUp(int player)          // Routine to score up a point, check everything
{
  int otherplayer;
  if (player ==1) {otherplayer = 0;} else otherplayer = 1;             
  Score[player]++;                 // Give the player a point
  DisplayScore();                   // Show current score
  if ((Score[player] >= ScoreTo) && (Score[player] - Score[otherplayer] > 1))   // You have to win by 2 points
  {GameOver(player);                // was it a win?
  } else
     if ((Score[Player1] + Score[Player2]) % ServeTo == 0){
       Player1isServing = !Player1isServing;
       PlaySound(SoundChangeServe);
       DisplayServe(Player1isServing);
      }

}  // end of ScoreUp

void DisplayScore(){                                                      // Update displays for score in the game
                                                                          // Show player1 score on the left and player 2 on the right
   if (Score[Player1] > 9){ matrix.writeDigitNum(0,Score[Player1]/10);} else matrix.writeDigitRaw(0,0);     // use leading blank not zero
   matrix.writeDigitNum(1,Score[Player1]%10);                             
   if (Score[Player2] > 9){ matrix.writeDigitNum(3,Score[Player2]/10);} else matrix.writeDigitRaw(3,0);
   matrix.writeDigitNum(4,Score[Player2]%10);
   matrix.drawColon(false);                                                  // no colon
   matrix.writeDisplay();                                                   // and push out the LED print
}    // end of DisplayScore

void DisplayGames(){                                                  // Update displays for games in the match
   if (Games[Player1] > 9 ) {matrix1.writeDigitNum(0,Games[Player1]/10);} else matrix1.writeDigitRaw(0,0);
   matrix1.writeDigitNum(1,Games[Player1]%10);                             
   if (Games[Player2] > 9 ) {matrix1.writeDigitNum(3,Games[Player2]/10);} else matrix1.writeDigitRaw(0,0);
   matrix1.writeDigitNum(4,Games[Player2]%10);
   matrix1.drawColon(false);                                                  // no colon
   matrix1.writeDisplay();                                                   // and push out the LED print
}     // end of DisplayGames

void DisplayServe(bool firstplayer){                                // Update displays for who is serving
    int colort;
    if (firstplayer) {colort = Green;} else colort = Yellow;          // change color for each player
    mclear(Line2);                                                       // Clear display lines on 16 x 32 panel
    PrintPanel("Serve", Line1, colort);
    Arrows(firstplayer,Line2,colort);                              // shows to the left (Player1) or right (Player2)
                                                                   // depending on boolean flag
}     // end of DisplayServe


void PlaySound(int WhichSound){                                // Plays appropriate Sound file
       SoundBusy = true;                                        // Are we already playing a sound?  
       while (SoundBusy){                                       // Then wait til its done
       SoundBusyValue = digitalRead(SoundBusyPin);             // as indicated by ACT going High again
       if (SoundBusyValue == LOW) {
       delay(SoundBusyDelay);                                   // wait on the soundboard
         } else     
            SoundBusy = false;} 
      digitalWrite(FirstSoundPin+WhichSound, LOW);              //Play the sound
      delay(fxset);                                             // hold to start the sound
      digitalWrite(FirstSoundPin+WhichSound, HIGH);              //Turn off the sound
      delay(fxset);                                              // Ensure its set
}     // end of PlaySound


void GameOver(int player){                                // Someone has won the game
    int gocolor;
    mclear(Line1);
    mclear(Line2);
    Games[player]++;
    DisplayGames();
    PlaySound(SoundGameOver);
      Flash ("Game", "Over!");
    if (player == Player1) {
      if (!JI) {PlaySound(SoundWinnerIs1);
      ScrollText2("Player 1", "Wins ",  Yellow, Yellow);} else {PlaySound(SoundWinnerIsJoan);
      Flash("Joan", "Wins!");}
      Player1isServing = false;} else
        {
         if (!JI) {PlaySound(SoundWinnerIs2);
         ScrollText2("Player 2", "Wins ",  Yellow, Yellow);} else {PlaySound(SoundWinnerIsIrwin);
         Flash("Irwin", "Wins!");}
         Player1isServing = true;}
    Score[Player1] =0;                                // reset scores and display
    Score[Player2] = 0;
    DisplayScore();
    if (player == Player1){
    if (!JI) {ScrollText2("Player 2", "Serve",  Yellow, Yellow);}
        else ScrollText2("Irwin", "Serve",  Yellow, Yellow);}
        else if (!JI) {ScrollText2("Player 1", "Serve",  Yellow, Yellow);} else ScrollText2("Joan", "Serve",  Yellow, Yellow);
   DisplayGameNumber();                                   // Game number called out
   PlaySound(SoundNewGame);
   DisplayServe(Player1isServing);
}     // end of Game Over

byte check_switches()            // This will return pin pressed and includes a debounce
{
 
  int reading;
  int button_pressed = 0;
  int index;
  
 for (index = 0; index < numbuttons; index++) {
    reading = digitalRead(firstbuttonpin + index);
  if (reading == LOW && lastreading[index] == HIGH)     // switch pressed?
   {button_pressed = index + 1;                         // calculate return number
    delay(DEBOUNCE);                                    // debounce a little
    break;                                              // end "for" loop
   }
   lastreading[index] = reading;                         //update reading                   
  }
 // return switch number (1 - numbuttons)
    return button_pressed;
}     // end of Check Switches

void Bounce(int bouncetimes)
{
  int x;
  int y;
  int jj;
  matrixp.fillScreen(Black);                               // clear the screen
  matrixp.fillRect(2,15,28,1,Green);                      // draw the table
  matrixp.fillRect(15,9,1,6,White);                        // and the net
  for (int i = 0; i <=bouncetimes; i++) 
  { jj = -1;                                                // reset y index (will increment to 0, first element)
  for (int j = firstx; j <=lastx; j++) 
  {     jj++;                                               // increment y coordinate index
         x = j;                                               // bounce left to right
         y = bouncey[jj];
         matrixp.fillCircle(x,y,ballsize,Yellow);
         if (jj == 0){                                        // display the paddle
        matrixp.fillRect(0,0,2,6,Green);
        delay(bouncedelay);
        }
        delay(bouncedelay);
       matrixp.fillCircle(x,y,ballsize,Black);
       delay(bouncedelay);
        matrixp.fillRect(0,0,2,6,Black);
   }
        jj = bouncesize+1;
   for (int j = lastx; j >=firstx; j--)                             // same except in reverse
  {     jj-- ;
        x = j;
        y = bouncey[jj];
        matrixp.fillCircle(x,y,ballsize,Yellow);
        delay(bouncedelay);
        if (jj == bouncesize){                                    // display the paddle
        matrixp.fillRect(30,0,2,6,Green);
        delay(bouncedelay * 3);
        } 
       matrixp.fillCircle(x,y,ballsize,Black);
        delay(bouncedelay * 3);
         matrixp.fillRect(30,0,2,6,Black);
   }
}
} // end of Bounce

void PrintPanel(String ToPrint, int pline,  int pcolort) 
{
      if (pline == Line1) {matrixp.setCursor(1,0);} else matrixp.setCursor(1,mh/2);
      mclear(pline);                                    // clear the line out
      matrixp.setTextColor(pcolort);                    // set the color
      matrixp.setTextWrap(true);                        // shouldn't matter
      matrixp.print(ToPrint);                           // print the line
} // end of PrintPanel


void ScrollText(String ToScroll, int pline, int pcolort) {
    int yaxis;
    int pixelx = mw;                                              // start scroll at 'right end'
    if (pline == 0) {yaxis = 0;} else yaxis = mh/2;              // y axis varies with line 0 or 1 so set it
    matrixp.setTextWrap(false);                               // we don't wrap text so it scrolls nicely
     int numchars = ToScroll.length();                        // number of characters in string
     for (int ff = 0; ff <= cw*numchars; ff++) {                    // start appropriately and scroll left
        matrixp.setCursor(pixelx,yaxis);
        pixelx-- ;
        mclear(pline);                                        // clear the line
        matrixp.setTextColor(pcolort);                        // set the color
        matrixp.print(ToScroll);                              // and print (once for each pixel)
        delay(scrolldelayc);
    }
        delay(afterscrolldelay);
} // end of display_scrollText

void ScrollText2(String ToScroll1, String ToScroll2, int pcolort1,int pcolort2) {
                                                                  // scrolls two strings at once in parallel
    int pixelx = mw;                                              // start at right hand side
    int pixelcount;                                               // will be total (longer) pixel count
    int pixelcount1;                                              // counter for string 1
    int pixelcount2;                                              // counter for string 2
    matrixp.setTextWrap(false);                               // we don't wrap text so it scrolls nicely
     pixelcount1 = ToScroll1.length()*cw;                        // number of characters in line 0 string converted to pixels
     pixelcount2 = ToScroll2.length()*cw;                        // number of characters in line 1 string converted to pixels
     if (pixelcount2 > pixelcount1) {pixelcount = pixelcount2;} else pixelcount = pixelcount1;   // set for longer of two strings
     
     for (int ff = 0; ff <= pixelcount; ff++) {                    // start appropriately and scroll left (longer of two strings)
     if (pixelcount1 >= 0) {                                      // Print top line
       mclear(0);                                                 // clear the line
       matrixp.setCursor(pixelx,0);                               // position
       matrixp.setTextColor(pcolort1);                        // set the color
       matrixp.print(ToScroll1);                                // and print (once for each pixel )
       pixelcount1--;}                                          // and decrement
     if (pixelcount2 >= 0) {                                      // Print 2nd line (one may finish before the other!)
       mclear(1);                                             // clear the line
       matrixp.setCursor(pixelx,mh/2);                                   // reposition
       matrixp.setTextColor(pcolort2);                        // set the color
       matrixp.print(ToScroll2);                               // and print (once for each pixel )
       pixelcount2--;}                                          // and decrement
       pixelx-- ;
       delay(scrolldelayc);
    }
       delay(afterscrolldelay);       
} // end of ScrollText2

void mclear(int pline) {                                       // quick clear of either line 0 or line 1
    int xc;
    int yc;
    if (pline == 0) {yc = 0;} else yc = mh/2;
     matrixp.fillRect(0,yc,mw,mh/2,Black);
}  // end of mclear

void Arrows(bool left, int pline, int pcolort) {
    int yaxis;                                                // y axis varies with line 0 or 1
    if (pline == 0) {yaxis = 0;} else yaxis = mh/2;              // so set it
    if (left) {                                                       // called with player1 true or not (i.e., player2)
      matrixp.fillTriangle(1,4+yaxis,4,1+yaxis,4, 7+yaxis, pcolort);  // arrows point right or left indicating
      matrixp.fillRect(5,3+yaxis,6,3,pcolort);                            // who is serving
      matrixp.fillTriangle(12,4+yaxis,15,1+yaxis,15, 7+yaxis, pcolort);
      matrixp.fillRect(16,3+yaxis,6,3,pcolort);
      matrixp.fillTriangle(23,4+yaxis,26,1+yaxis,26, 7+yaxis, pcolort);
      matrixp.fillRect(27,3+yaxis,6,3,pcolort); } else
      {
        matrixp.fillTriangle(30,4+yaxis,27,1+yaxis,27, 7+yaxis, pcolort);
        matrixp.fillRect(21,3+yaxis,6,3,pcolort);
        matrixp.fillTriangle(19,4+yaxis,16,1+yaxis,16, 7+yaxis, pcolort);
        matrixp.fillRect(10,3+yaxis,6,3,pcolort);
        matrixp.fillTriangle(8,4+yaxis,5,1+yaxis,5, 7+yaxis, pcolort);
        matrixp.fillRect(0,3+yaxis,5,3,pcolort); 
      }
      
} // End of Arrows

void Flash (String ToFlash1, String ToFlash2){
      const int flashcolorsize = 9;
      int flashcolors[flashcolorsize] = {Green, Cyan, Yellow, Magenta, Red, Blue,Green, Red, White};
      for (int i = 0; i < flashcolorsize; i++){
      matrixp.fillScreen (Black);
      matrixp.setCursor(0,0);                                   // reposition
      matrixp.setTextColor(flashcolors[i]);                        // set the color
      matrixp.print(ToFlash1);                               // and print (once for each pixel )
      matrixp.setCursor(0,mh/2);                                   // reposition
      matrixp.print(ToFlash2);                               // and print (once for each pixel )
      delay(flashdelay);                                        // wait after flash
      }
} // end of Flash

void BeginMatch(){
    DisplayScore();                                       // show LED Score
    DisplayGames();                                     // for game and games/match
    PlaySound(SoundStartup);                            // Play startup sound on soundcard  
    Bounce(1);                                          // Play the cute startup graphic
    PlaySound(SoundLetsPlay);  
    ScrollText2("Let's Ping ","Play  Pong ", Yellow, Yellow);   // and little warmup
    PlaySound(SoundNewGame);                            // New game sound
    DisplayGameNumber();                                    // Should be "Game #1"
    DisplayServe(Player1isServing);                       // and give initial serve indication (play 1 or 2)
} // End of Begin Match

 void DisplaySetandServe(){
    if (digitalRead(ScoreToSwitch) == HIGH){ScoreTo = 21;} else ScoreTo = 11 ;         // Check settings for Score and Serve
    if (digitalRead(ServeToSwitch) == HIGH){ServeTo = 5;} else ServeTo = 2 ;            // in case it changes
    if (digitalRead(JISwitch) == HIGH){JI = true;} else JI = false ;                   //  Convenient to read JI switch too
    lcd.setCursor(0, 0);                                // set LCD column 0, row 0
    lcd.print("Score of " );
    lcd.print(ScoreTo);                               // print either 21 or 11
    lcd.print(" wins" );
    lcd.setCursor(0, 1);                                // set LCD column 0, row 1
    lcd.print("Service:");                         
    lcd.print(ServeTo);                                     // print either 5 or 2
    lcd.print("  ");
    if (JI) {lcd.print("J&I  ");} else lcd.print("P1/P2");    // indicate whether the Joan & Irwin over-ride is in place or Player 1 and 2
} // end of Display Set and Serve

void DisplayGameNumber(){
    PrintPanel("Game", Line1, Green);
    mclear(1);
    matrixp.setCursor((mw/2)-3,mh/2);                         // 2nd line is which game we are on
    matrixp.print("# ");
    matrixp.setCursor((mw/2)+4,mh/2);
    matrixp.print(Games[0]+Games[1]+1,DEC );                           // print the line
    delay(delaygamenumber);
}

Schematics

Ping Pong Scoreboard Connections
All the connections used
ping_pong_score_board_connections_WEpZbu3che.docx

Comments

Similar projects you might like

Arduino Ping Pong Pinball

Project tutorial by joesinstructables

  • 5,825 views
  • 4 comments
  • 12 respects

ARDUINO PONG

Project tutorial by raul

  • 2,648 views
  • 1 comment
  • 8 respects

Arduino Controlled Pinball Machine

Project tutorial by Bob Blomquist

  • 39,893 views
  • 49 comments
  • 115 respects

RING PONG

Project showcase by aerodynamics

  • 7,675 views
  • 2 comments
  • 15 respects

Levitate a Ping Pong Ball Using Arduino and Simulink

Project tutorial by DanDoherty

  • 7,544 views
  • 4 comments
  • 11 respects

Chiming, Scrolling LED Matrix Clock

Project tutorial by goveil1

  • 3,530 views
  • 5 comments
  • 21 respects
Add projectSign up / Login