Components and supplies
Arduino Mega 2560
Single Pole Single Throw Toggle Switches
AdaFruit RGB 16x32 matrix
Adafruit Seven Segment 1.5" LED Red
Standard LCD - 16x2 White on Blue
Speaker: 0.25W, 8 ohms
Pushbutton Switch, Momentary
Big Lighted NO Pushbuttons
Adafruit FX SoundBoard 16mb
Project description
Code
Ping Pong Scoreboard Code
arduino
The ping pong scoreboard sketch
1 2 3 4/* Ping Pong Scorekeeper 5 Device to take care of scoring, switch of serve, keeping track of games won, etc. 6 Configurable for different rule changes (end of game, switching serves, etc.) 7 Includes sound effects 8 */ 9 10 11#include <Wire.h> 12#include <RGBmatrixPanel.h> 13#include <LiquidCrystal_I2C.h> 14#include <Adafruit_GFX.h> 15#include "Adafruit_LEDBackpack.h" 16 17 18 19 20Adafruit_7segment matrix = Adafruit_7segment(); // Score display 21Adafruit_7segment matrix1 = Adafruit_7segment(); // Games/match display 22LiquidCrystal_I2C lcd(0x27,16,2); // initialize LCD 23 24 25int DEBOUNCE =250; // button debouncer 26int which_button; // return from button query 27const int ScoreToSwitch = A8; // the number of pin for switch for "11 or 21" as end of game 28const int ServeToSwitch = A9; // number of pin for switch for "Serve 2 or Serve 5" before switching 29const int JISwitch = A10; // Switches between Joan and Irwin vs. Player 1 and Player 2 30const int firstbuttonpin = 2; // Use pins 2-7 for reading the scoring buttons 31const int numbuttons = 6; //number of buttons to check 32int lastreading[numbuttons]; // for button read routine 33const int scrolldelay = 500; // time to pause between scrolled text items 34const int delaygamenumber = 2000; // milliseconds to let "Game #xx" linger 35 36// Most of the signal pins for the 16 x 32 RGB matrix are configurable, but the CLK pin has some 37// special constraints. On 8-bit AVR boards it must be on PORTB... 38// Pin 8 works on the Arduino Uno & compatibles (e.g. Adafruit Metro), 39// Pin 11 works on the Arduino Mega. On 32-bit SAMD boards it must be 40// on the same PORT as the RGB data pins (D2-D7)... 41// Pin 8 works on the Adafruit Metro M0 or Arduino Zero, 42// Pin A4 works on the Adafruit Metro M4 (if using the Adafruit RGB 43// Matrix Shield, cut trace between CLK pads and run a wire to A4). 44 45//#define CLK 8 // USE THIS ON ARDUINO UNO, ADAFRUIT METRO M0, etc. 46//#define CLK A4 // USE THIS ON METRO M4 (not M0) 47#define CLK 11 // USE THIS ON ARDUINO MEGA 48#define OE 9 49#define LAT 10 50#define A A0 51#define B A1 52#define C A2 53 54int Red = 0xF800; // Color definitions for RGB panel 55int Black = 0x0000; 56int Yellow = 0xFFE0; 57int Green = 0x07E0; 58int White = 0xFFF; 59int Blue = 0x001F; 60int Cyan = 0x07E0; 61int Magenta = 0xF81F; 62 63 64// Define RGB matrix width and height. 65const int mw = 32; //matrix width in pixels 66const int mh = 16; // total matrix height in pixels 67const int cw = 6; // width of a character (size 1) in pixels 68const int Line1 = 0; // first line of RGB panel (just easier to remember) 69const int Line2 = 1; // second line 70const int scrolldelayc = 100; // time between successive characters in scroll 71const int flashdelay = 1000; // delay for flash display 72const int afterscrolldelay = 2000; // when finished with scroll, leave it up a bit 73 74const int bouncedelay = 45; 75const int bouncesize = 25; 76const int firstx = 3; // x coordinate for first and last ping pong ball position 77const int lastx = 28; // for start up graphic routine 78const 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 79const int ballsize = 1; // 2 looks nicer but gives a GIANT ball! 80 81 82RGBmatrixPanel matrixp(A, B, C, CLK, LAT, OE, false); // initialize 16 x 32 matrix 83 84 85/* 86 * Definition of buttons 87 * 1 Reset Match and Game 88 * 2 Force switch of service (but don't reset game) 89 * 3 Score point for Player 1 90 * 4 Unscore point for Player 1 (it was a mistake or disputed,etc.) 91 * 5 Score point for Player 2 92 * 6 Unscore point for Player 2 93 94 */ 95 96int Score[2]; //Score 97int Games[2]; // Games won 98bool Player1isServing; // flag for which play has serve 99int ScoreTo = 21; // game length will be 11 or 21 100int ServeTo = 5; // serve length will be 2 or 5 101bool JI = false; // assume generic players 102int switchread; 103int i; 104const int Player1 = 0; // Position in array for first player/team 105const int Player2 = 1; // Same for "other" player 106 107// Voice/sound files 108/* 109 The sounds are all on the FX Sound board in the form of sequential WAV files 110 that are name T0 through T6 (board has a maximum of 0-10 (11) files in this form (GPIO) 111 */ 112 113const int SoundStartup = 0; // Sound to play at startup Ping Pong ball then Shall We Play a Game 114const int SoundChangeServe = 1; // Simple bell or tone 115const int SoundNewGame = 2; // "New Game" 116const int SoundGameOver = 3; // Game Over + Cheering 117const int SoundWinnerIs1 = 4; // And the Winner is Player 1 118const int SoundWinnerIs2 = 5; // And the Winner is Player 2 119const int SoundUndo = 6; // Point was a mistake 120const int SoundWinnerIsJoan = 7; // And the winner is Joan 121const int SoundWinnerIsIrwin = 8; // same for Irwin 122const int SoundLetsPlay = 9; // Lets Play Ping Pong 123 124 125 126const int FirstSoundPin = 36; // first pin to use for soundboard (0-10 available on soundboard); 127const int SizeSoundPin = 10; // number of pins used (36-45) 128const int fxset = 150; // Time soundboard must be held LOW to register (documentation says 125 ms) 129const int SoundBusyPin = 8; // Soundboard pin that goes LOW when board is active 130const int SoundBusyDelay = 25; // Period to wait between checking to see if sound has finished 131int SoundBusyValue; 132bool SoundBusy; // set true when busy 133 134 135void setup() { 136 137 which_button = 0; 138 for (i = firstbuttonpin; i < firstbuttonpin+numbuttons; i++) { 139 pinMode(i, INPUT_PULLUP); 140 lastreading[i] = HIGH;} // initialize all buttons 'off' 141 pinMode(ScoreToSwitch, INPUT_PULLUP); // These three are toggle switches that change 142 pinMode (ServeToSwitch, INPUT_PULLUP); // the rules of the game (Win Score [11 or 21] and Serve change frequency [2 or 5]) 143 pinMode (JISwitch, INPUT_PULLUP); // and "Joan and Irwin" vs. "Player 1 and Player" 144 pinMode(SoundBusyPin, INPUT_PULLUP); 145 146 for (i=FirstSoundPin; i <FirstSoundPin+SizeSoundPin; i++){ // pins for sound board 147 pinMode(i,OUTPUT); // each an output 148 digitalWrite(i, HIGH); } // and initialize high (off) 149 Player1isServing = true; // default, can be switched with button 2 (switch serve) 150 for (i=0; i <2; i++) { // clear score and matches 151 Score[i] = 0; 152 Games[i] = 0; 153 } 154 Serial.begin (9600); 155 Wire.begin(); // initialize I2C interface 156 matrix.begin(0x70); // initialize Score LED 157 matrix1.begin(0x71); // and Game LED 158 matrixp.begin(); // initialize 16x32 RGB panel 159 matrixp.fillScreen(Black); // and clear it 160 matrix1.setBrightness(10); // mid level brightness for Game display (0-15) 161 lcd.init(); // initialize LCD 162 lcd.backlight(); 163 164 DisplaySetandServe(); // Initial Displays for LCD 165 BeginMatch(); // and begin the match with "Game #" 166 167 168} // End of Setup 169 170void loop() { 171 // put your main code here, to run repeatedly: 172 173 DisplaySetandServe(); // Check the settings of "Play to" and "Each Service" 174 175which_button = check_switches(); 176 if (which_button != 0) { 177 178 switch (which_button) { 179 case 0: // no button pushed 180 break; 181 182 case 1: // Set Zero to Match and Game Score 183 for (i=0; i <2; i++) { 184 Score[i] = 0; 185 Games[i] = 0;} 186 BeginMatch(); 187 break; 188 189 case 2: // Just switch serve and don't reset game 190 Player1isServing = !Player1isServing; 191 PlaySound(SoundChangeServe); 192 DisplayServe(Player1isServing); 193 break; 194 195 case 3: // Point for Player 1 196 ScoreUp(Player1); 197 break; 198 199 case 4: // That was a mistake so 'unscore' 200 if (Score[Player1] > 0) {Score[Player1]--;} 201 PlaySound(SoundUndo); 202 DisplayScore(); 203 break; 204 205 case 5: // Point for Player 2 206 ScoreUp(Player2); 207 break; 208 209 case 6: // That was a mistake so 'unscore' 210 if (Score[Player2] > 0) {Score[Player2]--;} 211 PlaySound(SoundUndo); 212 DisplayScore(); 213 break; 214 215 default: 216 Serial.println("Should never get here!"); 217 break; 218 219} // End of Switch 220 } // end of "if non-zero button" 221 222} // end of Void Loop 223 224void ScoreUp(int player) // Routine to score up a point, check everything 225{ 226 int otherplayer; 227 if (player ==1) {otherplayer = 0;} else otherplayer = 1; 228 Score[player]++; // Give the player a point 229 DisplayScore(); // Show current score 230 if ((Score[player] >= ScoreTo) && (Score[player] - Score[otherplayer] > 1)) // You have to win by 2 points 231 {GameOver(player); // was it a win? 232 } else 233 if ((Score[Player1] + Score[Player2]) % ServeTo == 0){ 234 Player1isServing = !Player1isServing; 235 PlaySound(SoundChangeServe); 236 DisplayServe(Player1isServing); 237 } 238 239} // end of ScoreUp 240 241void DisplayScore(){ // Update displays for score in the game 242 // Show player1 score on the left and player 2 on the right 243 if (Score[Player1] > 9){ matrix.writeDigitNum(0,Score[Player1]/10);} else matrix.writeDigitRaw(0,0); // use leading blank not zero 244 matrix.writeDigitNum(1,Score[Player1]%10); 245 if (Score[Player2] > 9){ matrix.writeDigitNum(3,Score[Player2]/10);} else matrix.writeDigitRaw(3,0); 246 matrix.writeDigitNum(4,Score[Player2]%10); 247 matrix.drawColon(false); // no colon 248 matrix.writeDisplay(); // and push out the LED print 249} // end of DisplayScore 250 251void DisplayGames(){ // Update displays for games in the match 252 if (Games[Player1] > 9 ) {matrix1.writeDigitNum(0,Games[Player1]/10);} else matrix1.writeDigitRaw(0,0); 253 matrix1.writeDigitNum(1,Games[Player1]%10); 254 if (Games[Player2] > 9 ) {matrix1.writeDigitNum(3,Games[Player2]/10);} else matrix1.writeDigitRaw(0,0); 255 matrix1.writeDigitNum(4,Games[Player2]%10); 256 matrix1.drawColon(false); // no colon 257 matrix1.writeDisplay(); // and push out the LED print 258} // end of DisplayGames 259 260void DisplayServe(bool firstplayer){ // Update displays for who is serving 261 int colort; 262 if (firstplayer) {colort = Green;} else colort = Yellow; // change color for each player 263 mclear(Line2); // Clear display lines on 16 x 32 panel 264 PrintPanel("Serve", Line1, colort); 265 Arrows(firstplayer,Line2,colort); // shows to the left (Player1) or right (Player2) 266 // depending on boolean flag 267} // end of DisplayServe 268 269 270void PlaySound(int WhichSound){ // Plays appropriate Sound file 271 SoundBusy = true; // Are we already playing a sound? 272 while (SoundBusy){ // Then wait til its done 273 SoundBusyValue = digitalRead(SoundBusyPin); // as indicated by ACT going High again 274 if (SoundBusyValue == LOW) { 275 delay(SoundBusyDelay); // wait on the soundboard 276 } else 277 SoundBusy = false;} 278 digitalWrite(FirstSoundPin+WhichSound, LOW); //Play the sound 279 delay(fxset); // hold to start the sound 280 digitalWrite(FirstSoundPin+WhichSound, HIGH); //Turn off the sound 281 delay(fxset); // Ensure its set 282} // end of PlaySound 283 284 285void GameOver(int player){ // Someone has won the game 286 int gocolor; 287 mclear(Line1); 288 mclear(Line2); 289 Games[player]++; 290 DisplayGames(); 291 PlaySound(SoundGameOver); 292 Flash ("Game", "Over!"); 293 if (player == Player1) { 294 if (!JI) {PlaySound(SoundWinnerIs1); 295 ScrollText2("Player 1", "Wins ", Yellow, Yellow);} else {PlaySound(SoundWinnerIsJoan); 296 Flash("Joan", "Wins!");} 297 Player1isServing = false;} else 298 { 299 if (!JI) {PlaySound(SoundWinnerIs2); 300 ScrollText2("Player 2", "Wins ", Yellow, Yellow);} else {PlaySound(SoundWinnerIsIrwin); 301 Flash("Irwin", "Wins!");} 302 Player1isServing = true;} 303 Score[Player1] =0; // reset scores and display 304 Score[Player2] = 0; 305 DisplayScore(); 306 if (player == Player1){ 307 if (!JI) {ScrollText2("Player 2", "Serve", Yellow, Yellow);} 308 else ScrollText2("Irwin", "Serve", Yellow, Yellow);} 309 else if (!JI) {ScrollText2("Player 1", "Serve", Yellow, Yellow);} else ScrollText2("Joan", "Serve", Yellow, Yellow); 310 DisplayGameNumber(); // Game number called out 311 PlaySound(SoundNewGame); 312 DisplayServe(Player1isServing); 313} // end of Game Over 314 315byte check_switches() // This will return pin pressed and includes a debounce 316{ 317 318 int reading; 319 int button_pressed = 0; 320 int index; 321 322 for (index = 0; index < numbuttons; index++) { 323 reading = digitalRead(firstbuttonpin + index); 324 if (reading == LOW && lastreading[index] == HIGH) // switch pressed? 325 {button_pressed = index + 1; // calculate return number 326 delay(DEBOUNCE); // debounce a little 327 break; // end "for" loop 328 } 329 lastreading[index] = reading; //update reading 330 } 331 // return switch number (1 - numbuttons) 332 return button_pressed; 333} // end of Check Switches 334 335void Bounce(int bouncetimes) 336{ 337 int x; 338 int y; 339 int jj; 340 matrixp.fillScreen(Black); // clear the screen 341 matrixp.fillRect(2,15,28,1,Green); // draw the table 342 matrixp.fillRect(15,9,1,6,White); // and the net 343 for (int i = 0; i <=bouncetimes; i++) 344 { jj = -1; // reset y index (will increment to 0, first element) 345 for (int j = firstx; j <=lastx; j++) 346 { jj++; // increment y coordinate index 347 x = j; // bounce left to right 348 y = bouncey[jj]; 349 matrixp.fillCircle(x,y,ballsize,Yellow); 350 if (jj == 0){ // display the paddle 351 matrixp.fillRect(0,0,2,6,Green); 352 delay(bouncedelay); 353 } 354 delay(bouncedelay); 355 matrixp.fillCircle(x,y,ballsize,Black); 356 delay(bouncedelay); 357 matrixp.fillRect(0,0,2,6,Black); 358 } 359 jj = bouncesize+1; 360 for (int j = lastx; j >=firstx; j--) // same except in reverse 361 { jj-- ; 362 x = j; 363 y = bouncey[jj]; 364 matrixp.fillCircle(x,y,ballsize,Yellow); 365 delay(bouncedelay); 366 if (jj == bouncesize){ // display the paddle 367 matrixp.fillRect(30,0,2,6,Green); 368 delay(bouncedelay * 3); 369 } 370 matrixp.fillCircle(x,y,ballsize,Black); 371 delay(bouncedelay * 3); 372 matrixp.fillRect(30,0,2,6,Black); 373 } 374} 375} // end of Bounce 376 377void PrintPanel(String ToPrint, int pline, int pcolort) 378{ 379 if (pline == Line1) {matrixp.setCursor(1,0);} else matrixp.setCursor(1,mh/2); 380 mclear(pline); // clear the line out 381 matrixp.setTextColor(pcolort); // set the color 382 matrixp.setTextWrap(true); // shouldn't matter 383 matrixp.print(ToPrint); // print the line 384} // end of PrintPanel 385 386 387void ScrollText(String ToScroll, int pline, int pcolort) { 388 int yaxis; 389 int pixelx = mw; // start scroll at 'right end' 390 if (pline == 0) {yaxis = 0;} else yaxis = mh/2; // y axis varies with line 0 or 1 so set it 391 matrixp.setTextWrap(false); // we don't wrap text so it scrolls nicely 392 int numchars = ToScroll.length(); // number of characters in string 393 for (int ff = 0; ff <= cw*numchars; ff++) { // start appropriately and scroll left 394 matrixp.setCursor(pixelx,yaxis); 395 pixelx-- ; 396 mclear(pline); // clear the line 397 matrixp.setTextColor(pcolort); // set the color 398 matrixp.print(ToScroll); // and print (once for each pixel) 399 delay(scrolldelayc); 400 } 401 delay(afterscrolldelay); 402} // end of display_scrollText 403 404void ScrollText2(String ToScroll1, String ToScroll2, int pcolort1,int pcolort2) { 405 // scrolls two strings at once in parallel 406 int pixelx = mw; // start at right hand side 407 int pixelcount; // will be total (longer) pixel count 408 int pixelcount1; // counter for string 1 409 int pixelcount2; // counter for string 2 410 matrixp.setTextWrap(false); // we don't wrap text so it scrolls nicely 411 pixelcount1 = ToScroll1.length()*cw; // number of characters in line 0 string converted to pixels 412 pixelcount2 = ToScroll2.length()*cw; // number of characters in line 1 string converted to pixels 413 if (pixelcount2 > pixelcount1) {pixelcount = pixelcount2;} else pixelcount = pixelcount1; // set for longer of two strings 414 415 for (int ff = 0; ff <= pixelcount; ff++) { // start appropriately and scroll left (longer of two strings) 416 if (pixelcount1 >= 0) { // Print top line 417 mclear(0); // clear the line 418 matrixp.setCursor(pixelx,0); // position 419 matrixp.setTextColor(pcolort1); // set the color 420 matrixp.print(ToScroll1); // and print (once for each pixel ) 421 pixelcount1--;} // and decrement 422 if (pixelcount2 >= 0) { // Print 2nd line (one may finish before the other!) 423 mclear(1); // clear the line 424 matrixp.setCursor(pixelx,mh/2); // reposition 425 matrixp.setTextColor(pcolort2); // set the color 426 matrixp.print(ToScroll2); // and print (once for each pixel ) 427 pixelcount2--;} // and decrement 428 pixelx-- ; 429 delay(scrolldelayc); 430 } 431 delay(afterscrolldelay); 432} // end of ScrollText2 433 434void mclear(int pline) { // quick clear of either line 0 or line 1 435 int xc; 436 int yc; 437 if (pline == 0) {yc = 0;} else yc = mh/2; 438 matrixp.fillRect(0,yc,mw,mh/2,Black); 439} // end of mclear 440 441void Arrows(bool left, int pline, int pcolort) { 442 int yaxis; // y axis varies with line 0 or 1 443 if (pline == 0) {yaxis = 0;} else yaxis = mh/2; // so set it 444 if (left) { // called with player1 true or not (i.e., player2) 445 matrixp.fillTriangle(1,4+yaxis,4,1+yaxis,4, 7+yaxis, pcolort); // arrows point right or left indicating 446 matrixp.fillRect(5,3+yaxis,6,3,pcolort); // who is serving 447 matrixp.fillTriangle(12,4+yaxis,15,1+yaxis,15, 7+yaxis, pcolort); 448 matrixp.fillRect(16,3+yaxis,6,3,pcolort); 449 matrixp.fillTriangle(23,4+yaxis,26,1+yaxis,26, 7+yaxis, pcolort); 450 matrixp.fillRect(27,3+yaxis,6,3,pcolort); } else 451 { 452 matrixp.fillTriangle(30,4+yaxis,27,1+yaxis,27, 7+yaxis, pcolort); 453 matrixp.fillRect(21,3+yaxis,6,3,pcolort); 454 matrixp.fillTriangle(19,4+yaxis,16,1+yaxis,16, 7+yaxis, pcolort); 455 matrixp.fillRect(10,3+yaxis,6,3,pcolort); 456 matrixp.fillTriangle(8,4+yaxis,5,1+yaxis,5, 7+yaxis, pcolort); 457 matrixp.fillRect(0,3+yaxis,5,3,pcolort); 458 } 459 460} // End of Arrows 461 462void Flash (String ToFlash1, String ToFlash2){ 463 const int flashcolorsize = 9; 464 int flashcolors[flashcolorsize] = {Green, Cyan, Yellow, Magenta, Red, Blue,Green, Red, White}; 465 for (int i = 0; i < flashcolorsize; i++){ 466 matrixp.fillScreen (Black); 467 matrixp.setCursor(0,0); // reposition 468 matrixp.setTextColor(flashcolors[i]); // set the color 469 matrixp.print(ToFlash1); // and print (once for each pixel ) 470 matrixp.setCursor(0,mh/2); // reposition 471 matrixp.print(ToFlash2); // and print (once for each pixel ) 472 delay(flashdelay); // wait after flash 473 } 474} // end of Flash 475 476void BeginMatch(){ 477 DisplayScore(); // show LED Score 478 DisplayGames(); // for game and games/match 479 PlaySound(SoundStartup); // Play startup sound on soundcard 480 Bounce(1); // Play the cute startup graphic 481 PlaySound(SoundLetsPlay); 482 ScrollText2("Let's Ping ","Play Pong ", Yellow, Yellow); // and little warmup 483 PlaySound(SoundNewGame); // New game sound 484 DisplayGameNumber(); // Should be "Game #1" 485 DisplayServe(Player1isServing); // and give initial serve indication (play 1 or 2) 486} // End of Begin Match 487 488 void DisplaySetandServe(){ 489 if (digitalRead(ScoreToSwitch) == HIGH){ScoreTo = 21;} else ScoreTo = 11 ; // Check settings for Score and Serve 490 if (digitalRead(ServeToSwitch) == HIGH){ServeTo = 5;} else ServeTo = 2 ; // in case it changes 491 if (digitalRead(JISwitch) == HIGH){JI = true;} else JI = false ; // Convenient to read JI switch too 492 lcd.setCursor(0, 0); // set LCD column 0, row 0 493 lcd.print("Score of " ); 494 lcd.print(ScoreTo); // print either 21 or 11 495 lcd.print(" wins" ); 496 lcd.setCursor(0, 1); // set LCD column 0, row 1 497 lcd.print("Service:"); 498 lcd.print(ServeTo); // print either 5 or 2 499 lcd.print(" "); 500 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 501} // end of Display Set and Serve 502 503void DisplayGameNumber(){ 504 PrintPanel("Game", Line1, Green); 505 mclear(1); 506 matrixp.setCursor((mw/2)-3,mh/2); // 2nd line is which game we are on 507 matrixp.print("# "); 508 matrixp.setCursor((mw/2)+4,mh/2); 509 matrixp.print(Games[0]+Games[1]+1,DEC ); // print the line 510 delay(delaygamenumber); 511} 512
Ping Pong Scoreboard Code
arduino
The ping pong scoreboard sketch
1 2 3 4/* Ping Pong Scorekeeper 5 Device to take care of scoring, switch of serve, keeping track of games won, etc. 6 Configurable for different rule changes (end of game, switching serves, etc.) 7 Includes sound effects 8 */ 9 10 11#include <Wire.h> 12#include <RGBmatrixPanel.h> 13#include <LiquidCrystal_I2C.h> 14#include <Adafruit_GFX.h> 15#include "Adafruit_LEDBackpack.h" 16 17 18 19 20Adafruit_7segment matrix = Adafruit_7segment(); // Score display 21Adafruit_7segment matrix1 = Adafruit_7segment(); // Games/match display 22LiquidCrystal_I2C lcd(0x27,16,2); // initialize LCD 23 24 25int DEBOUNCE =250; // button debouncer 26int which_button; // return from button query 27const int ScoreToSwitch = A8; // the number of pin for switch for "11 or 21" as end of game 28const int ServeToSwitch = A9; // number of pin for switch for "Serve 2 or Serve 5" before switching 29const int JISwitch = A10; // Switches between Joan and Irwin vs. Player 1 and Player 2 30const int firstbuttonpin = 2; // Use pins 2-7 for reading the scoring buttons 31const int numbuttons = 6; //number of buttons to check 32int lastreading[numbuttons]; // for button read routine 33const int scrolldelay = 500; // time to pause between scrolled text items 34const int delaygamenumber = 2000; // milliseconds to let "Game #xx" linger 35 36// Most of the signal pins for the 16 x 32 RGB matrix are configurable, but the CLK pin has some 37// special constraints. On 8-bit AVR boards it must be on PORTB... 38// Pin 8 works on the Arduino Uno & compatibles (e.g. Adafruit Metro), 39// Pin 11 works on the Arduino Mega. On 32-bit SAMD boards it must be 40// on the same PORT as the RGB data pins (D2-D7)... 41// Pin 8 works on the Adafruit Metro M0 or Arduino Zero, 42// Pin A4 works on the Adafruit Metro M4 (if using the Adafruit RGB 43// Matrix Shield, cut trace between CLK pads and run a wire to A4). 44 45//#define CLK 8 // USE THIS ON ARDUINO UNO, ADAFRUIT METRO M0, etc. 46//#define CLK A4 // USE THIS ON METRO M4 (not M0) 47#define CLK 11 // USE THIS ON ARDUINO MEGA 48#define OE 9 49#define LAT 10 50#define A A0 51#define B A1 52#define C A2 53 54int Red = 0xF800; // Color definitions for RGB panel 55int Black = 0x0000; 56int Yellow = 0xFFE0; 57int Green = 0x07E0; 58int White = 0xFFF; 59int Blue = 0x001F; 60int Cyan = 0x07E0; 61int Magenta = 0xF81F; 62 63 64// Define RGB matrix width and height. 65const int mw = 32; //matrix width in pixels 66const int mh = 16; // total matrix height in pixels 67const int cw = 6; // width of a character (size 1) in pixels 68const int Line1 = 0; // first line of RGB panel (just easier to remember) 69const int Line2 = 1; // second line 70const int scrolldelayc = 100; // time between successive characters in scroll 71const int flashdelay = 1000; // delay for flash display 72const int afterscrolldelay = 2000; // when finished with scroll, leave it up a bit 73 74const int bouncedelay = 45; 75const int bouncesize = 25; 76const int firstx = 3; // x coordinate for first and last ping pong ball position 77const int lastx = 28; // for start up graphic routine 78const 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 79const int ballsize = 1; // 2 looks nicer but gives a GIANT ball! 80 81 82RGBmatrixPanel matrixp(A, B, C, CLK, LAT, OE, false); // initialize 16 x 32 matrix 83 84 85/* 86 * Definition of buttons 87 * 1 Reset Match and Game 88 * 2 Force switch of service (but don't reset game) 89 * 3 Score point for Player 1 90 * 4 Unscore point for Player 1 (it was a mistake or disputed,etc.) 91 * 5 Score point for Player 2 92 * 6 Unscore point for Player 2 93 94 */ 95 96int Score[2]; //Score 97int Games[2]; // Games won 98bool Player1isServing; // flag for which play has serve 99int ScoreTo = 21; // game length will be 11 or 21 100int ServeTo = 5; // serve length will be 2 or 5 101bool JI = false; // assume generic players 102int switchread; 103int i; 104const int Player1 = 0; // Position in array for first player/team 105const int Player2 = 1; // Same for "other" player 106 107// Voice/sound files 108/* 109 The sounds are all on the FX Sound board in the form of sequential WAV files 110 that are name T0 through T6 (board has a maximum of 0-10 (11) files in this form (GPIO) 111 */ 112 113const int SoundStartup = 0; // Sound to play at startup Ping Pong ball then Shall We Play a Game 114const int SoundChangeServe = 1; // Simple bell or tone 115const int SoundNewGame = 2; // "New Game" 116const int SoundGameOver = 3; // Game Over + Cheering 117const int SoundWinnerIs1 = 4; // And the Winner is Player 1 118const int SoundWinnerIs2 = 5; // And the Winner is Player 2 119const int SoundUndo = 6; // Point was a mistake 120const int SoundWinnerIsJoan = 7; // And the winner is Joan 121const int SoundWinnerIsIrwin = 8; // same for Irwin 122const int SoundLetsPlay = 9; // Lets Play Ping Pong 123 124 125 126const int FirstSoundPin = 36; // first pin to use for soundboard (0-10 available on soundboard); 127const int SizeSoundPin = 10; // number of pins used (36-45) 128const int fxset = 150; // Time soundboard must be held LOW to register (documentation says 125 ms) 129const int SoundBusyPin = 8; // Soundboard pin that goes LOW when board is active 130const int SoundBusyDelay = 25; // Period to wait between checking to see if sound has finished 131int SoundBusyValue; 132bool SoundBusy; // set true when busy 133 134 135void setup() { 136 137 which_button = 0; 138 for (i = firstbuttonpin; i < firstbuttonpin+numbuttons; i++) { 139 pinMode(i, INPUT_PULLUP); 140 lastreading[i] = HIGH;} // initialize all buttons 'off' 141 pinMode(ScoreToSwitch, INPUT_PULLUP); // These three are toggle switches that change 142 pinMode (ServeToSwitch, INPUT_PULLUP); // the rules of the game (Win Score [11 or 21] and Serve change frequency [2 or 5]) 143 pinMode (JISwitch, INPUT_PULLUP); // and "Joan and Irwin" vs. "Player 1 and Player" 144 pinMode(SoundBusyPin, INPUT_PULLUP); 145 146 for (i=FirstSoundPin; i <FirstSoundPin+SizeSoundPin; i++){ // pins for sound board 147 pinMode(i,OUTPUT); // each an output 148 digitalWrite(i, HIGH); } // and initialize high (off) 149 Player1isServing = true; // default, can be switched with button 2 (switch serve) 150 for (i=0; i <2; i++) { // clear score and matches 151 Score[i] = 0; 152 Games[i] = 0; 153 } 154 Serial.begin (9600); 155 Wire.begin(); // initialize I2C interface 156 matrix.begin(0x70); // initialize Score LED 157 matrix1.begin(0x71); // and Game LED 158 matrixp.begin(); // initialize 16x32 RGB panel 159 matrixp.fillScreen(Black); // and clear it 160 matrix1.setBrightness(10); // mid level brightness for Game display (0-15) 161 lcd.init(); // initialize LCD 162 lcd.backlight(); 163 164 DisplaySetandServe(); // Initial Displays for LCD 165 BeginMatch(); // and begin the match with "Game #" 166 167 168} // End of Setup 169 170void loop() { 171 // put your main code here, to run repeatedly: 172 173 DisplaySetandServe(); // Check the settings of "Play to" and "Each Service" 174 175which_button = check_switches(); 176 if (which_button != 0) { 177 178 switch (which_button) { 179 case 0: // no button pushed 180 break; 181 182 case 1: // Set Zero to Match and Game Score 183 for (i=0; i <2; i++) { 184 Score[i] = 0; 185 Games[i] = 0;} 186 BeginMatch(); 187 break; 188 189 case 2: // Just switch serve and don't reset game 190 Player1isServing = !Player1isServing; 191 PlaySound(SoundChangeServe); 192 DisplayServe(Player1isServing); 193 break; 194 195 case 3: // Point for Player 1 196 ScoreUp(Player1); 197 break; 198 199 case 4: // That was a mistake so 'unscore' 200 if (Score[Player1] > 0) {Score[Player1]--;} 201 PlaySound(SoundUndo); 202 DisplayScore(); 203 break; 204 205 case 5: // Point for Player 2 206 ScoreUp(Player2); 207 break; 208 209 case 6: // That was a mistake so 'unscore' 210 if (Score[Player2] > 0) {Score[Player2]--;} 211 PlaySound(SoundUndo); 212 DisplayScore(); 213 break; 214 215 default: 216 Serial.println("Should never get here!"); 217 break; 218 219} // End of Switch 220 } // end of "if non-zero button" 221 222} // end of Void Loop 223 224void ScoreUp(int player) // Routine to score up a point, check everything 225{ 226 int otherplayer; 227 if (player ==1) {otherplayer = 0;} else otherplayer = 1; 228 Score[player]++; // Give the player a point 229 DisplayScore(); // Show current score 230 if ((Score[player] >= ScoreTo) && (Score[player] - Score[otherplayer] > 1)) // You have to win by 2 points 231 {GameOver(player); // was it a win? 232 } else 233 if ((Score[Player1] + Score[Player2]) % ServeTo == 0){ 234 Player1isServing = !Player1isServing; 235 PlaySound(SoundChangeServe); 236 DisplayServe(Player1isServing); 237 } 238 239} // end of ScoreUp 240 241void DisplayScore(){ // Update displays for score in the game 242 // Show player1 score on the left and player 2 on the right 243 if (Score[Player1] > 9){ matrix.writeDigitNum(0,Score[Player1]/10);} else matrix.writeDigitRaw(0,0); // use leading blank not zero 244 matrix.writeDigitNum(1,Score[Player1]%10); 245 if (Score[Player2] > 9){ matrix.writeDigitNum(3,Score[Player2]/10);} else matrix.writeDigitRaw(3,0); 246 matrix.writeDigitNum(4,Score[Player2]%10); 247 matrix.drawColon(false); // no colon 248 matrix.writeDisplay(); // and push out the LED print 249} // end of DisplayScore 250 251void DisplayGames(){ // Update displays for games in the match 252 if (Games[Player1] > 9 ) {matrix1.writeDigitNum(0,Games[Player1]/10);} else matrix1.writeDigitRaw(0,0); 253 matrix1.writeDigitNum(1,Games[Player1]%10); 254 if (Games[Player2] > 9 ) {matrix1.writeDigitNum(3,Games[Player2]/10);} else matrix1.writeDigitRaw(0,0); 255 matrix1.writeDigitNum(4,Games[Player2]%10); 256 matrix1.drawColon(false); // no colon 257 matrix1.writeDisplay(); // and push out the LED print 258} // end of DisplayGames 259 260void DisplayServe(bool firstplayer){ // Update displays for who is serving 261 int colort; 262 if (firstplayer) {colort = Green;} else colort = Yellow; // change color for each player 263 mclear(Line2); // Clear display lines on 16 x 32 panel 264 PrintPanel("Serve", Line1, colort); 265 Arrows(firstplayer,Line2,colort); // shows to the left (Player1) or right (Player2) 266 // depending on boolean flag 267} // end of DisplayServe 268 269 270void PlaySound(int WhichSound){ // Plays appropriate Sound file 271 SoundBusy = true; // Are we already playing a sound? 272 while (SoundBusy){ // Then wait til its done 273 SoundBusyValue = digitalRead(SoundBusyPin); // as indicated by ACT going High again 274 if (SoundBusyValue == LOW) { 275 delay(SoundBusyDelay); // wait on the soundboard 276 } else 277 SoundBusy = false;} 278 digitalWrite(FirstSoundPin+WhichSound, LOW); //Play the sound 279 delay(fxset); // hold to start the sound 280 digitalWrite(FirstSoundPin+WhichSound, HIGH); //Turn off the sound 281 delay(fxset); // Ensure its set 282} // end of PlaySound 283 284 285void GameOver(int player){ // Someone has won the game 286 int gocolor; 287 mclear(Line1); 288 mclear(Line2); 289 Games[player]++; 290 DisplayGames(); 291 PlaySound(SoundGameOver); 292 Flash ("Game", "Over!"); 293 if (player == Player1) { 294 if (!JI) {PlaySound(SoundWinnerIs1); 295 ScrollText2("Player 1", "Wins ", Yellow, Yellow);} else {PlaySound(SoundWinnerIsJoan); 296 Flash("Joan", "Wins!");} 297 Player1isServing = false;} else 298 { 299 if (!JI) {PlaySound(SoundWinnerIs2); 300 ScrollText2("Player 2", "Wins ", Yellow, Yellow);} else {PlaySound(SoundWinnerIsIrwin); 301 Flash("Irwin", "Wins!");} 302 Player1isServing = true;} 303 Score[Player1] =0; // reset scores and display 304 Score[Player2] = 0; 305 DisplayScore(); 306 if (player == Player1){ 307 if (!JI) {ScrollText2("Player 2", "Serve", Yellow, Yellow);} 308 else ScrollText2("Irwin", "Serve", Yellow, Yellow);} 309 else if (!JI) {ScrollText2("Player 1", "Serve", Yellow, Yellow);} else ScrollText2("Joan", "Serve", Yellow, Yellow); 310 DisplayGameNumber(); // Game number called out 311 PlaySound(SoundNewGame); 312 DisplayServe(Player1isServing); 313} // end of Game Over 314 315byte check_switches() // This will return pin pressed and includes a debounce 316{ 317 318 int reading; 319 int button_pressed = 0; 320 int index; 321 322 for (index = 0; index < numbuttons; index++) { 323 reading = digitalRead(firstbuttonpin + index); 324 if (reading == LOW && lastreading[index] == HIGH) // switch pressed? 325 {button_pressed = index + 1; // calculate return number 326 delay(DEBOUNCE); // debounce a little 327 break; // end "for" loop 328 } 329 lastreading[index] = reading; //update reading 330 } 331 // return switch number (1 - numbuttons) 332 return button_pressed; 333} // end of Check Switches 334 335void Bounce(int bouncetimes) 336{ 337 int x; 338 int y; 339 int jj; 340 matrixp.fillScreen(Black); // clear the screen 341 matrixp.fillRect(2,15,28,1,Green); // draw the table 342 matrixp.fillRect(15,9,1,6,White); // and the net 343 for (int i = 0; i <=bouncetimes; i++) 344 { jj = -1; // reset y index (will increment to 0, first element) 345 for (int j = firstx; j <=lastx; j++) 346 { jj++; // increment y coordinate index 347 x = j; // bounce left to right 348 y = bouncey[jj]; 349 matrixp.fillCircle(x,y,ballsize,Yellow); 350 if (jj == 0){ // display the paddle 351 matrixp.fillRect(0,0,2,6,Green); 352 delay(bouncedelay); 353 } 354 delay(bouncedelay); 355 matrixp.fillCircle(x,y,ballsize,Black); 356 delay(bouncedelay); 357 matrixp.fillRect(0,0,2,6,Black); 358 } 359 jj = bouncesize+1; 360 for (int j = lastx; j >=firstx; j--) // same except in reverse 361 { jj-- ; 362 x = j; 363 y = bouncey[jj]; 364 matrixp.fillCircle(x,y,ballsize,Yellow); 365 delay(bouncedelay); 366 if (jj == bouncesize){ // display the paddle 367 matrixp.fillRect(30,0,2,6,Green); 368 delay(bouncedelay * 3); 369 } 370 matrixp.fillCircle(x,y,ballsize,Black); 371 delay(bouncedelay * 3); 372 matrixp.fillRect(30,0,2,6,Black); 373 } 374} 375} // end of Bounce 376 377void PrintPanel(String ToPrint, int pline, int pcolort) 378{ 379 if (pline == Line1) {matrixp.setCursor(1,0);} else matrixp.setCursor(1,mh/2); 380 mclear(pline); // clear the line out 381 matrixp.setTextColor(pcolort); // set the color 382 matrixp.setTextWrap(true); // shouldn't matter 383 matrixp.print(ToPrint); // print the line 384} // end of PrintPanel 385 386 387void ScrollText(String ToScroll, int pline, int pcolort) { 388 int yaxis; 389 int pixelx = mw; // start scroll at 'right end' 390 if (pline == 0) {yaxis = 0;} else yaxis = mh/2; // y axis varies with line 0 or 1 so set it 391 matrixp.setTextWrap(false); // we don't wrap text so it scrolls nicely 392 int numchars = ToScroll.length(); // number of characters in string 393 for (int ff = 0; ff <= cw*numchars; ff++) { // start appropriately and scroll left 394 matrixp.setCursor(pixelx,yaxis); 395 pixelx-- ; 396 mclear(pline); // clear the line 397 matrixp.setTextColor(pcolort); // set the color 398 matrixp.print(ToScroll); // and print (once for each pixel) 399 delay(scrolldelayc); 400 } 401 delay(afterscrolldelay); 402} // end of display_scrollText 403 404void ScrollText2(String ToScroll1, String ToScroll2, int pcolort1,int pcolort2) { 405 // scrolls two strings at once in parallel 406 int pixelx = mw; // start at right hand side 407 int pixelcount; // will be total (longer) pixel count 408 int pixelcount1; // counter for string 1 409 int pixelcount2; // counter for string 2 410 matrixp.setTextWrap(false); // we don't wrap text so it scrolls nicely 411 pixelcount1 = ToScroll1.length()*cw; // number of characters in line 0 string converted to pixels 412 pixelcount2 = ToScroll2.length()*cw; // number of characters in line 1 string converted to pixels 413 if (pixelcount2 > pixelcount1) {pixelcount = pixelcount2;} else pixelcount = pixelcount1; // set for longer of two strings 414 415 for (int ff = 0; ff <= pixelcount; ff++) { // start appropriately and scroll left (longer of two strings) 416 if (pixelcount1 >= 0) { // Print top line 417 mclear(0); // clear the line 418 matrixp.setCursor(pixelx,0); // position 419 matrixp.setTextColor(pcolort1); // set the color 420 matrixp.print(ToScroll1); // and print (once for each pixel ) 421 pixelcount1--;} // and decrement 422 if (pixelcount2 >= 0) { // Print 2nd line (one may finish before the other!) 423 mclear(1); // clear the line 424 matrixp.setCursor(pixelx,mh/2); // reposition 425 matrixp.setTextColor(pcolort2); // set the color 426 matrixp.print(ToScroll2); // and print (once for each pixel ) 427 pixelcount2--;} // and decrement 428 pixelx-- ; 429 delay(scrolldelayc); 430 } 431 delay(afterscrolldelay); 432} // end of ScrollText2 433 434void mclear(int pline) { // quick clear of either line 0 or line 1 435 int xc; 436 int yc; 437 if (pline == 0) {yc = 0;} else yc = mh/2; 438 matrixp.fillRect(0,yc,mw,mh/2,Black); 439} // end of mclear 440 441void Arrows(bool left, int pline, int pcolort) { 442 int yaxis; // y axis varies with line 0 or 1 443 if (pline == 0) {yaxis = 0;} else yaxis = mh/2; // so set it 444 if (left) { // called with player1 true or not (i.e., player2) 445 matrixp.fillTriangle(1,4+yaxis,4,1+yaxis,4, 7+yaxis, pcolort); // arrows point right or left indicating 446 matrixp.fillRect(5,3+yaxis,6,3,pcolort); // who is serving 447 matrixp.fillTriangle(12,4+yaxis,15,1+yaxis,15, 7+yaxis, pcolort); 448 matrixp.fillRect(16,3+yaxis,6,3,pcolort); 449 matrixp.fillTriangle(23,4+yaxis,26,1+yaxis,26, 7+yaxis, pcolort); 450 matrixp.fillRect(27,3+yaxis,6,3,pcolort); } else 451 { 452 matrixp.fillTriangle(30,4+yaxis,27,1+yaxis,27, 7+yaxis, pcolort); 453 matrixp.fillRect(21,3+yaxis,6,3,pcolort); 454 matrixp.fillTriangle(19,4+yaxis,16,1+yaxis,16, 7+yaxis, pcolort); 455 matrixp.fillRect(10,3+yaxis,6,3,pcolort); 456 matrixp.fillTriangle(8,4+yaxis,5,1+yaxis,5, 7+yaxis, pcolort); 457 matrixp.fillRect(0,3+yaxis,5,3,pcolort); 458 } 459 460} // End of Arrows 461 462void Flash (String ToFlash1, String ToFlash2){ 463 const int flashcolorsize = 9; 464 int flashcolors[flashcolorsize] = {Green, Cyan, Yellow, Magenta, Red, Blue,Green, Red, White}; 465 for (int i = 0; i < flashcolorsize; i++){ 466 matrixp.fillScreen (Black); 467 matrixp.setCursor(0,0); // reposition 468 matrixp.setTextColor(flashcolors[i]); // set the color 469 matrixp.print(ToFlash1); // and print (once for each pixel ) 470 matrixp.setCursor(0,mh/2); // reposition 471 matrixp.print(ToFlash2); // and print (once for each pixel ) 472 delay(flashdelay); // wait after flash 473 } 474} // end of Flash 475 476void BeginMatch(){ 477 DisplayScore(); // show LED Score 478 DisplayGames(); // for game and games/match 479 PlaySound(SoundStartup); // Play startup sound on soundcard 480 Bounce(1); // Play the cute startup graphic 481 PlaySound(SoundLetsPlay); 482 ScrollText2("Let's Ping ","Play Pong ", Yellow, Yellow); // and little warmup 483 PlaySound(SoundNewGame); // New game sound 484 DisplayGameNumber(); // Should be "Game #1" 485 DisplayServe(Player1isServing); // and give initial serve indication (play 1 or 2) 486} // End of Begin Match 487 488 void DisplaySetandServe(){ 489 if (digitalRead(ScoreToSwitch) == HIGH){ScoreTo = 21;} else ScoreTo = 11 ; // Check settings for Score and Serve 490 if (digitalRead(ServeToSwitch) == HIGH){ServeTo = 5;} else ServeTo = 2 ; // in case it changes 491 if (digitalRead(JISwitch) == HIGH){JI = true;} else JI = false ; // Convenient to read JI switch too 492 lcd.setCursor(0, 0); // set LCD column 0, row 0 493 lcd.print("Score of " ); 494 lcd.print(ScoreTo); // print either 21 or 11 495 lcd.print(" wins" ); 496 lcd.setCursor(0, 1); // set LCD column 0, row 1 497 lcd.print("Service:"); 498 lcd.print(ServeTo); // print either 5 or 2 499 lcd.print(" "); 500 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 501} // end of Display Set and Serve 502 503void DisplayGameNumber(){ 504 PrintPanel("Game", Line1, Green); 505 mclear(1); 506 matrixp.setCursor((mw/2)-3,mh/2); // 2nd line is which game we are on 507 matrixp.print("# "); 508 matrixp.setCursor((mw/2)+4,mh/2); 509 matrixp.print(Games[0]+Games[1]+1,DEC ); // print the line 510 delay(delaygamenumber); 511} 512
Downloadable files
Ping Pong Scoreboard Connections
All the connections used
Ping Pong Scoreboard Connections
Comments
Only logged in users can leave comments
spudnut1
0 Followers
•0 Projects
Table of contents
Intro
5
0