// // Ver. 1.0 10/31/2014 // First release of Scoreboard Control Program // // -------------------------------------------------------------------- // A scoreboard was created for the game of Horseshoes. // Built in two halves, the left half covers courts 1, 2 and 3, while the right // half covers courts 4, 5 and 6. // Each half uses one Arduino Nano and is independent of the other half. // See photo: http://i351.photobucket.com/albums/q452/plclark3/IMJ_4700x.jpg // // This sketch is intended for use with the following hardware: // (one) Arduino USB Nano ATmega328 // (six) 16x32 Dot Matrix Display (DMD) panels // (six) External switches, normally open, connected to pins A0-A5 // // The DMD panels are arranged in pairs: 2-across and 3-down. // Each individual panel displays a 2-digit score, 6 inches high, using the full // 16-rows of LED's. // // The external foot switches are operated by the players themselves. // When a switch is closed, the corresponding pin goes LOW. // If switch is released within "delayPeriod" time, the score is incremented by 1. // If switch is held closed longer than "delayPeriod", the score is repeatedly // decremented by 1 until the switch is released, or the score reaches zero . // If an adjacent pair of switches are closed longer than "delayPeriod", both // scores are reset to zero. // When either score of an adjacent pair exceeds "maxScore", both scores are res et // to zero. // // For anyone attempting to connect a monochrome 16x32 DMD panel to Arduino, the // following diagram may be helpful. The 16-pin header on the back of the D MD // panel is wired to the following pins: // // D9 |_1|_2| D6 // GND |_3|_4| D7 // |_5|_6| // (slot) |_7|_8| D13 (the orientation slot is on the left side of the header) // |_9|10| D8 // |11|12| D11 // |13|14| // |15|16| // // You must supply 5V power directly to the DMD panel as Arduino cannot provide // nearly enough current. Lighting up all 512 LED's in a panel with 1/4 // multiplexing takes about 7 amps at 5V. // // -------------------------------------------------------------------- #include <SPI.h> #include <DMD.h> #include <TimerOne.h> #include <PLCBigFont.h> #include <TimedAction.h> // Operational parameters int maxScore = 25; // highest score possible int debounce = 25; // debounce time in millis unsigned long delayPeriod = 700; // decrement delay period, in mil lis unsigned long repeatPeriod = 300; // decrement repeat period, in mi llis unsigned long currTime = 0; // current time since start up, i n millis unsigned long closedPeriod = 0; // switch-closed period in, milli s unsigned long closedTime [] = {0, 0, 0, 0, 0, 0}; // switch-closed time, in mill is volatile int interruptFlag = 0; // interrupt flag int refreshFlag = 0; // refresh flag int decTimerFlag = 0; // decrement flag int i = 0; // index for switches and scores (0 thru 5) int j = 0; // score pair indicator (0, 2 or 4) int k = 0; // display column offset (0, 64 o r 128) int prevStatus [] = {0, 0, 0, 0, 0, 0}; // prev switch status int currStatus [] = {1, 1, 1, 1, 1, 1}; // curr switch status int score [] = {0, 0, 0, 0, 0, 0}; // curr scores int scoreStatus [] = {0, 0, 0, 0, 0, 0}; // score status for decrementing // scoreStatus values: 0=switch open, 1=switch closed, 2=switch closed & score d ecremented //String PL; // used with Serial.println // Example: PL = ""; PL = PL + i + " time: " + decTime [i]; Serial.println(PL); DMD dmd(2,3); // (DISPLAYS_ACROSS, DISPLAYS_DOW N) // initialize a timer to perform decrementing TimedAction decTimer = TimedAction(100,checkDecrement); // do it every 100 mill is // -------------------------------------------------------------------- void ScanDMD() // timer interrupt to drive displ ay refresh { dmd.scanDisplayBySPI(); } // -------------------------------------------------------------------- ISR (PCINT1_vect) // pin change interrupt A0-A5 { interruptFlag = 1; // set interrupt flag } // -------------------------------------------------------------------- void setup () { //Serial.begin(9600); //Serial.println("Start"); dmd.clearScreen( true ); // clear random pixels on display dmd.selectFont(MyBigFont); // select large font // Initialize pin change interrupt (PCINT1_vect) PCMSK1 = 63; // set interrupt mask for pins A0 -A5 PCIFR |= bit (PCIF1); // clear any outstanding interrup ts PCICR |= bit (PCIE1); // enable pin change interrupts // Initialize DMD interrupt used to refresh the display // Set period in microseconds to call ScanDMD // Anything longer than 4000 (4ms) may cause the display to flicker Timer1.initialize( 5000 ); Timer1.attachInterrupt( ScanDMD ); // set switch pins HIGH digitalWrite (A0, HIGH); // enable pull-down pin 14 digitalWrite (A1, HIGH); // enable pull-down pin 15 digitalWrite (A2, HIGH); // enable pull-down pin 16 digitalWrite (A3, HIGH); // enable pull-down pin 17 digitalWrite (A4, HIGH); // enable pull-down pin 18 digitalWrite (A5, HIGH); // enable pull-down pin 19 refreshBoard (); // initialize scoreboard to zeros } // -------------------------------------------------------------------- void checkSwitches () // check status of switches { //Serial.println(""); //Serial.println("checkSwitches"); delay (debounce); // debounce switch contacts for (i = 0; i <= 5; i++) // quick loop to read pins A0-A5 { prevStatus [i] = currStatus [i]; // save switch status to prev currStatus [i] = digitalRead (A0 + i); // read current switch status // NOTE: an open switch reads HIGH (1); a closed switch reads LOW (0) } currTime = millis(); // get current time for (i = 0; i <= 5; i++) // loop to save switch-closed tim e { if (prevStatus [i] == 1 && currStatus [i] == 0) // if a switch just closed... { // Serial.println("switch closed"); closedTime [i] = currTime; // save the time scoreStatus [i] = 1; // switch is closed decTimerFlag = 1; // set decTimer flag // PL = ""; PL = PL + i + " scoreStatus: " + scoreStatus [i]; Serial.println (PL); // PL = ""; PL = PL + " decTimer: " + decTimerFlag; Serial.println (PL); } } refreshFlag = 0; // clear refresh flag for (i = 0; i <= 5; i++) // loop to increment scores { if (prevStatus [i] == 0 && currStatus [i] == 1) // if a switch just opened... { // Serial.println("switch opened"); // PL = ""; PL = PL + i + " scoreStatus: " + scoreStatus [i]; Serial.println (PL); if (scoreStatus [i] == 1) // but not yet decremented... { checkPeriod (); // check switch-closed period } scoreStatus [i] = 0; // status is open // PL = ""; PL = PL + i + " scoreStatus: " + scoreStatus [i]; Serial.println (PL); } } if (refreshFlag > 0) refreshBoard (); // refresh if a score changed interruptFlag = 0; // clear interrupt flag } // -------------------------------------------------------------------- void checkPeriod () // check switch-closed period { //Serial.println("checkPeriod"); closedPeriod = currTime - closedTime [i]; // calculate switch-closed period //PL = ""; PL = PL + i + " closedPeriod: " + closedPeriod; Serial.println(PL); if (closedPeriod < delayPeriod) // if closed period < delay perio d... { incrementScore (); // increment the score refreshFlag = 1; // set refresh flag } } // -------------------------------------------------------------------- void incrementScore () // increment score { //Serial.println("incrementScore"); j = (i / 2) * 2; // determine which pair of scores //PL = ""; PL = PL + i + " score: " + score [i]; Serial.println(PL); if (score [i] >= maxScore) // if score is at max... { score [j] = 0; // reset both scores to zero score [j + 1] = 0; } else { score [i] += 1; // else increment score by 1 } //PL = ""; PL = PL + i + " score: " + score [i]; Serial.println(PL); } // -------------------------------------------------------------------- void checkDecrement () // decrement score { //Serial.println(""); //Serial.println("checkDecrement"); refreshFlag = 0; // clear refresh flag decTimerFlag = 0; // clear decTimer flag currTime = millis(); // get current time for (i = 0; i <= 5; i = i + 2) // check scores for reset to zero { j = i + 1; // i and j are a pair of scores if (scoreStatus [i] == 2 && scoreStatus [j] == 2) // if both are decrementing. .. { score [i] = 0; score [j] = 0; // reset both scores to zero scoreStatus [i] = 0; scoreStatus [j] =0; // status is open refreshFlag = 1; // set refresh flag } } //i = 0; //PL = ""; PL = PL + i + " scoreStatus: " + scoreStatus [i]; Serial.println(PL) ; for (i = 0; i <= 5; i++) // loop to handle decrementing { if (scoreStatus [i] > 0) // if switch is closed... { decrementScore (); // decrement decTimerFlag = 1; // set decTimer flag } } //PL = ""; PL = PL + " decTimer: " + decTimerFlag; Serial.println(PL) ; if (refreshFlag > 0) refreshBoard (); // refresh if a score changed } // -------------------------------------------------------------------- void decrementScore () // decrement score { //Serial.println("decrementScore"); closedPeriod = currTime - closedTime [i]; // calculate switch-closed period //PL = ""; PL = PL + i + " scoreStatus: " + scoreStatus [i]; Serial.println(PL) ; //PL = ""; PL = PL + i + " closedPeriod: " + closedPeriod; Serial.println(PL) ; // If switch is closed and closedPeriod >= delay period OR // If score is decremented and closedPeriod >= repeat period...then decrement if ((scoreStatus [i] == 1 && closedPeriod >= delayPeriod) || (scoreStatus [i] == 2 && closedPeriod >= repeatPeriod)) { // PL = ""; PL = PL + i + " score: " + score [i]; Serial.println(PL); if (score [i] > 0) score [i] -= 1; // decrement score by 1 // PL = ""; PL = PL + i + " score: " + score [i]; Serial.println(PL); scoreStatus [i] = 2; // score decremented refreshFlag = 1; // set refresh flag closedTime [i] = currTime; // reset the switch-closed time } } // -------------------------------------------------------------------- void refreshBoard () // refresh entire display { //Serial.println("refreshBoard"); dmd.clearScreen( true ); // clear display first for (j = 0; j <= 5; j = j + 2) // display scores in pairs { displayPair (); } } // -------------------------------------------------------------------- void displayPair () // display a pair of scores { int scoreA = score [j]; // get a pair of scores int scoreB = score [j + 1]; char scoreA1 = scoreA / 10; // breakdown score to individual digits char scoreA2 = scoreA - (scoreA1 * 10); char scoreB1 = scoreB / 10; char scoreB2 = scoreB - (scoreB1 * 10); if (scoreA1 == 0) scoreA1 = 32; // if leading zero, replace with ASCII blank else scoreA1 += 48; // else add offset for ASCII zero scoreA2 += 48; // add offset for ASCII zero if (scoreB1 == 0) scoreB1 = 32; // if leading zero, replace with ASCII blank else scoreB1 += 48; // else add offset for ASCII zero
scoreB2 += 48; // add offset for ASCII zero
k = 32 - (j * 8); // k is the y coordinate (0, 16 o
r 32) dmd.drawChar( 0, k, scoreA1, GRAPHICS_NORMAL ); // display the scores dmd.drawChar( 12, k, scoreA2, GRAPHICS_NORMAL ); dmd.drawChar( 42, k, scoreB1, GRAPHICS_NORMAL ); dmd.drawChar( 54, k, scoreB2, GRAPHICS_NORMAL ); } // -------------------------------------------------------------------- void loop () // main loop { if (interruptFlag > 0) checkSwitches (); // check switches after an interr upt if (decTimerFlag > 0) decTimer.check(); // check for decrementing }