IR Remote Controlled Scoreboard :: Member Project
Arduino and Bean Bag Baseball? Yes – match made in heaven!
PEA customer, John Wren, built an IR Remote controlled Arduino powered scoreboard for his senior community bean bag baseball team. It includes tons of NEO pixels and looks great. Learn more about his project below!
Why did you build an Arduino scoreboard?
I live in a senior community and we play bean bag baseball. We keep score on a whiteboard. I thought it would be nice to have a more robust way of keeping score and I thought I might be able to sell some of the scoreboards if it works out the way I plan.
I recently retired from the industrial automation field and was bored and looking for a project, so I started to learn Arduino coding.
How does your project work?
The brains of the scoreboard uses an Arduino Mega. I selected the Mega because originally I was going to use buttons to control the score board.
Currently the scoreboard is controlled by a GE IR remote using NEC codes. The scoreboard has a score for each inning both for home team and visiting team and a total at the end.
The scores are 7 segment displays using neopixels driven from a single output pin from the Arduino.
What was your biggest struggle as you worked through this project?
I am fairly new to micro-controllers, my background is PLCs and HMIs my thought process didn’t match that of the infinite loop of the Arduino. I was able to learn about hardware interrupts (pin 2 interrupt 0 ) and switch case. My current code does not use the loop.
I found that a loop was necessary to make the compiler happy so the loop is there but empty. I have coded in many different languages in the past. When I went to school we programmed in machine language. C++ is new to me and I had a steep learning curve. some examples =vs==, missing{} and ;.
It has been a challenging but fun process!
The project is not yet finished but is coming out close to my original concept. So far the only major change is the interface. Using the IR remote as opposed to buttons.
Was the training at Programming Electronics Academy able to help you build your skill?
Yes!! I truly enjoy the presentation style. I always had questions after the lessons and tested alternate ways of the exercises.
About John:
John has been into electronics since he was in highschool, having picked up some experience from working in his father’s shop (who was a TV repairman). He is currently retired and spent a career working and teaching in the automation field for close to 40 years. He started in saw mills and has worked for independent engineering firms. Most of his career has been in the food manufacturing industry, having done work for companies such as Mars, Starbucks, E&J Gallo and a Micro Brewery in Delaware (Dogfish Head).
Arduino Code:
/* Program written by John wren (Wrengineering) 6/29/2020
This is designed to control a 6 Inning Beanbag baseball scoreboard via IR remote control
using a Pure Control universal remotemdlPC 100 and RCA code 1033
remote uses 32 bits in 2 groups of 16 in each group of 16 the lower group of 8 is the inverse
of the previous 8 so i utlized loByte to just use the value of the remote button that was pressed
The first 16 bits identify the contrller and the second contain the code for the button presed
this is sent in as a HEX value
I converted lowByte to a BIN and used it to compare for Switch Case
It will keep track of strikes, outs, men on base, runs/Inning and toal score
Utilizes Interupt 0 on Pin 2 to decode IR remote signals
Uses RGB led strips configured as seven segment displays for runs and total score
Also uses LED pixels for strikes,outs and men on base
Changed Seven_Segment_Pixel.h to match the order of segments on my display, in order tp make wiring simpler
Progress as of 7/7/2020 strike increment and decrement. outs increment and decrement.
Innings increment and decrement. Able to keep individual score based on Inning number
Need to add score decrement. Have only tested with 2 7 seven secment LEDs (need to solder)
with 16 7 segmet displays all set to the number 8 with a single color at full brightness
the FLA would be 5.6 20mA * (21 LEds per secment * 16 total Sements) the dual Power supply
has 6A at 5 volts and 3A at 12 volts at game start all scores set to zero FLa 5.28 I will
lower the intensity and that should lower the current draw. Measure current with all scores
This program will use a IR remote to control Strikes, Outs,Score, at bat team and inning.
The operator will be able to increment and decrement each value witht the remote. (see remote diagram for button info)
Currently using an Arduino but may switch to ESP 32 to add vioce and speed
***********************************TO DO NOTES**********************************
This is where I kept notes of what need to be completed or fixed
need to clear outs and strikes whith inning + or -
*/
//*****************************************************************************************
#include <IRremote.h>
#include <Adafruit_NeoPixel.h>
#include <Seven_Segment_Pixel.h>
//define pixel strip peramaters
#define PIN 36
#define NUMPIXELS 112
#define DIGITS 16
#define PIXPERSEG 1
#define NUMDELIMS 0
#define PIXPERDELIM 0
#define PINSO 35//STRIKE AND OUT PIXELS
#define NUMPIXELSSO 11//STRIKE, OUTS AND ON BASE PIXELS
//Score display 7 segment digits
Seven_Segment_Pixel display1 =
Seven_Segment_Pixel(DIGITS, PIXPERSEG, NUMDELIMS, PIXPERDELIM, NUMPIXELS,
PIN, NEO_BRG + NEO_KHZ800);
//************************************************************************
//Strike and Out Pixels
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELSSO, PINSO, NEO_GRB + NEO_KHZ800);
int RECV_PIN = 2;// IR input interupt 0
IRrecv irrecv(RECV_PIN);
decode_results results;
//************************************************************************************
//define Golbal variables
int Strikes = 0;
int Outs = 0;
int Inning = 1;
int Score = 0;
int gScore[7] {0, 0, 0, 0, 0, 0, 0,};
int hScore [7] {0, 0, 0, 0, 0, 0, 0,};
int gScoreTotal = 0 ;
int hScoreTotal = 0;
int gScoreTotalOnes = 0 ;
int hScoreTotalOnes = 0;
int gScoreTotalTens = 0 ;
int hScoreTotalTens = 0;
int digitIn[7] {0, 1, 2, 3, 4, 5, 6};
int runrR = 255;
int runrG = 0;
int runrB = 0;
byte lowbyte = 0;
byte gColor = 25;
byte hColor = 25;
bool atBat = 0;
bool On1st = 0;
bool On2nd = 0;
bool On3rd = 0;
//***************************************************************************************
void setup()
{
Serial.begin(9600);// rem this and all Sel.prints out after testing
//Starts Out and strike Pixels******************************************
pixels.begin();
//turns off all Strike LEDs
//****************************************
pixels.setPixelColor(0, 0, 0, 0);
pixels.setPixelColor(1, 0, 0, 0);
pixels.setPixelColor(2, 0, 0, 0);
//***************************************
//turns off all out LEDs
pixels.setPixelColor(3, 0, 0, 0);
pixels.setPixelColor(4, 0, 0, 0);
pixels.setPixelColor(5, 0, 0, 0);
//********************************************
//turns off allon base LEDs
pixels.setPixelColor(6, 0, 0, 0);
pixels.setPixelColor(7, 0, 0, 0);
pixels.setPixelColor(8, 0, 0, 0);
pixels.setPixelColor(6, 0, 255, 0);//turns on Guest at bat LED
pixels.show();
//**********Starts looking for IR signal on PIN 2********************************
attachInterrupt(0, CHECK_IR, CHANGE);
//Enables interupt and looks for value change(IR xmit button pushed)
irrecv.enableIRIn(); // Start the receiver and test to see if it is operational
//**********************************************************************************
//Turns on score digits and sets then to dim zero
display1.begin();//Starts pixel display
// Score Digits
//****************************R G B
//*****************Digit Value Color R,G,B
display1.updateDigit (8, 0 , (gColor * 5), 0 , 0 );
display1.updateDigit (7, 0 , (gColor * 5), 0 , 0 );
display1.updateDigit( 1, 0 , (gColor * 10), 0 , 0 );
display1.updateDigit( 2, 0 , gColor, 0 , 0 );
display1.updateDigit( 3, 0 , gColor, 0 , 0 );
display1.updateDigit( 4, 0 , gColor, 0 , 0 );
display1.updateDigit( 5, 0 , gColor, 0 , 0 );
display1.updateDigit( 6, 0 , gColor, 0 , 0 );
display1.updateDigit (16, 0 , 0, (hColor * 5) , 0 );
display1.updateDigit (15, 0 , 0, (hColor * 5) , 0 );
display1.updateDigit( 9, 0 , 0, hColor , 0 );
display1.updateDigit( 10, 0 , 0, hColor , 0 );
display1.updateDigit( 11, 0 , 0, hColor , 0 );
display1.updateDigit( 12, 0 , 0, hColor , 0 );
display1.updateDigit( 13, 0 , 0, hColor , 0 );
display1.updateDigit( 14, 0 , 0, hColor , 0 );
display1.show();
}//Setup
//********************Loop Not Used***********************************************
void loop()
{}
//*******************************************************************************
//Start of Interupt Switch Case from IR Xmit
//***************checks to see if IR ewciver has data and pares it down to a byte value
byte loByte = 0;
void CHECK_IR()
{
while (irrecv.decode(&results)) {
byte loByte = lowByte(results.value);// Switch case
irrecv.resume();
delay(20);
//**************************************************************
//Serial.print Area
//*****************************************************************
// sets where the switch gets its value loByte is the low byte value from IR xmitter
switch (loByte)
{
//****************************************************************
// Case0 Strike + Case Increases strike count updates display
case 147: //147 is the low byte value from IR xmiter when vol + button is pressed
if (Strikes < 4)
{
Strikes = (Strikes + 1); // increases the number of strike by one
}
if (Strikes == 4)
{
Strikes = 0;// reset strike count
pixels.setPixelColor(0, 0, 0, 0);
pixels.setPixelColor(1, 0, 0, 0);
pixels.setPixelColor(2, 0, 0, 0);
pixels.show();
if (Outs < 3)
{
Outs = (Outs + 1);
//Turns on Out LEDs
if (Outs != 0)
{
pixels.setPixelColor((6 - Outs), 0, 255, 0);// 6 - because of pixel numbering
pixels.show();
}
}
}
//Turns on strike LEDs
pixels.setPixelColor((Strikes - 1), 125, 255, 0);
pixels.show();
irrecv.resume();
break;
//***************************************************************************
//Case 1 Strike - Decreases strike count and updates display
case 234: //Serial.println("Vol down "); //234 is the low byte value from IR xmiter when vol - button is pressed
if (Strikes > 0)
{
Strikes = (Strikes - 1); // decreases the nuber of strike when button is pushed
}
//tuns off current strike pixel
pixels.setPixelColor((Strikes), 0, 0, 0);
pixels.show();
irrecv.resume();
break;
//***********************************************************************
//Case 2 Outs + Increases outs count updates display
case 39: // 39 is the low byte value from IR xmiter when Ch + button is pressed
if (Outs < 4)
{
Outs = (Outs + 1); // increases the number of outs by one
Strikes = 0;
//turns off strikes and lights next out LED
pixels.setPixelColor(0, 0, 0, 0);
pixels.setPixelColor(1, 0, 0, 0);
pixels.setPixelColor(2, 0, 0, 0);
if ((Outs == 1) || (Outs == 2) || (Outs == 3))
{
pixels.setPixelColor((6 - Outs ), 0, 255, 0);///?this line causes strike 3 to turn on when outs are reset
pixels.show();
}
if (Outs == 4)
{
Outs = 0;// reset Outs
//clears base runner LEDs
pixels.setPixelColor(8, 0, 0, 0);
pixels.setPixelColor(9, 0, 0, 0);
pixels.setPixelColor(10, 0, 0, 0);
pixels.show();
if (atBat == 1)
{
Inning = Inning + 1; // increases Inning at end of Inning
digitIn[Inning] = (digitIn[Inning]);
}
//Set out pixels to off
pixels.setPixelColor(3, 0, 0, 0);
pixels.setPixelColor(4, 0, 0, 0);
pixels.setPixelColor(5, 0, 0, 0);
pixels.show();
// switch base runner color to guest team
if (atBat == 0)
{
(atBat = 1);
(runrG = 255);
(runrR = 0);
(runrB = 0);
pixels.setPixelColor(7, 255, 0, 0);//Home at bat LED
pixels.setPixelColor(6, 0, 0, 0);//Gueds at bat LED
pixels.show();
display1.updateDigit ((Inning + 8), 0 , 0, (hColor * 10) , 0 );
display1.show();
}
// switch colors to home team
else
{
(atBat = 0);
(runrG = 0);
(runrR = 255);
(runrB = 0);
pixels.setPixelColor(7, 0, 0, 0);//Home at bat LED
pixels.setPixelColor(6, 0, 255, 0);//Guest at bat LED
pixels.show();
display1.updateDigit (Inning, 0 , (gColor * 10), 0 , 0 );
display1.show();
}
}
}
irrecv.resume();
break;
//*****************************************************************
//Case 3 Outs - Decreases outs count updates display
case 110: // 110 is the low byte value from IR xmiter when CH - button is pressed
if (Outs > 0)
{
Outs = (Outs - 1); // decreases the nuber of strike when button is pushed
}
//tuns off current Outs pixel
pixels.setPixelColor((5 - Outs), 0, 0, 0);
pixels.show();
irrecv.resume();
break;
//****************************************************************
//Case 4 score + Increases Score count, updates LED display
case 233: //233 is the low byte value from IR xmiter when #7 button is pressed
if (atBat == 0)
{
gScore[Inning] = (gScore[Inning] + 1);// increases guest score by one
if (gScore[Inning] > 9)
{
gScore[Inning] = 9;
}
//Adds total Guest Score together and seperate into two digits fpr 1s and 10s
gScoreTotal = gScore[1] + gScore[2] + gScore[3] + gScore[4] + gScore[5] + gScore[6];
gScoreTotalOnes = ( gScoreTotal % 10);
gScoreTotalTens = ( gScoreTotal / 10);
display1.updateDigit (Inning, (gScore[Inning]) , (gColor * 10), 0 , 0 );
display1.updateDigit (8, (gScoreTotalOnes) , 255, 0 , 0 );
display1.updateDigit(7, (gScoreTotalTens) , 255, 0 , 0 );
display1.show();
}
else if (atBat == 1)
{
hScore[Inning] = (hScore[Inning] + 1);
if (hScore[Inning] > 9)
{
hScore[Inning] = 9;
}
//Adds total Home Score together and seperate into two digits fpr 1s and 10s
hScoreTotal = hScore[1] + hScore[2] + hScore[3] + hScore[4] + hScore[5] + hScore[6];
hScoreTotalOnes = ( hScoreTotal % 10);
hScoreTotalTens = ( hScoreTotal / 10);
display1.updateDigit (16, (hScoreTotalOnes) , 0, 255 , 0 );
display1.updateDigit (15, (hScoreTotalTens) , 0, 255 , 0 );
display1.updateDigit ((Inning + 8), (hScore[Inning]) , 0, (hColor * 10) , 0 );
display1.show();
}
irrecv.resume();
break;
//********************************************************************
//Case 5 Score - Decreases Score count updates display
case 225: // 225 is the low byte value from IR xmiter when #9 button is pressed
if (atBat == 0)
{
gScore[Inning] = (gScore[Inning] - 1);// increases guest score by one
if (gScore[Inning] < 1)
{
gScore[Inning] = 0;
} //Adds total Guest Score together and seperate into two digits fpr 1s and 10s
gScoreTotal = gScore[1] + gScore[2] + gScore[3] + gScore[4] + gScore[5] + gScore[6];
gScoreTotalOnes = ( gScoreTotal % 10);
gScoreTotalTens = ( gScoreTotal / 10);
display1.updateDigit (8, (gScoreTotalOnes) , 255, 0 , 0 );
display1.updateDigit(7, (gScoreTotalTens) , 255, 0 , 0 );
display1.updateDigit ((Inning ), (gScore[Inning]) , (gColor * 10), 0 , 0 ); // current inning score
display1.show();
}
else if (atBat == 1)
{
hScore[Inning] = (hScore[Inning] - 1);
if (hScore[Inning] < 1)
{
hScore[Inning] = 0;
} //Adds total Home Score together and seperate into two digits fpr 1s and 10s
hScoreTotal = hScore[1] + hScore[2] + hScore[3] + hScore[4] + hScore[5] + hScore[6];
hScoreTotalOnes = ( hScoreTotal % 10);
hScoreTotalTens = ( hScoreTotal / 10);
display1.updateDigit (16, (hScoreTotalOnes) , 0, 255 , 0 );
display1.updateDigit (15, (hScoreTotalTens) , 0, 255 , 0 );
display1.updateDigit ((Inning + 8), (hScore[Inning]) , 0, (hColor * 10) , 0 );
display1.show();
}
irrecv.resume();
break;
//**************************************************************************
//Case 6 Inning +Increases Inning count updates display
case 85: //85 is the low byte value from IR xmiter when number one button is pressed
if ((Inning < 6) && (atBat == 1))
{
Inning = (Inning + 1); // increases the number of Inning by one
digitIn[Inning] = (digitIn[Inning]);
atBat = 0;
pixels.setPixelColor(7, 0, 0, 0);//Home at bat LED
pixels.setPixelColor(6, 0, 255, 0);//Guest at bat LED
pixels.show();
display1.updateDigit (Inning, (gScore[Inning]) , (gColor * 10), 0 , 0 );
display1.show();
(runrG = 0);//colors for base runners
(runrR = 255);
(runrB = 0);
}
else
{
atBat = 1;
display1.updateDigit (Inning, (gScore[Inning]) , (gColor * 10), 0 , 0 );
pixels.setPixelColor(7, 255, 0, 0);//Home at bat LED
pixels.setPixelColor(6, 0, 0, 0);//Guest at bat LED
pixels.show();
display1.updateDigit (Inning, (gScore[Inning]) , (gColor * 10), 0 , 0 );
display1.updateDigit ((Inning + 8), (hScore[Inning]) , 0, (hColor * 10) , 0 );
display1.show();
(runrG = 255);//colors for base runners
(runrR = 0);
(runrB = 0);
}
if (Inning >= 6)
{
(Inning = 6);
}
//turns off all Strike LEDs
//****************************************
pixels.setPixelColor(0, 0, 0, 0);
pixels.setPixelColor(1, 0, 0, 0);
pixels.setPixelColor(2, 0, 0, 0);
//***************************************
//turns off all out LEDs
pixels.setPixelColor(3, 0, 0, 0);
pixels.setPixelColor(4, 0, 0, 0);
pixels.setPixelColor(5, 0, 0, 0);
//********************************************
//clears base runner LEDs
pixels.setPixelColor(8, 0, 0, 0);
pixels.setPixelColor(9, 0, 0, 0);
pixels.setPixelColor(10, 0, 0, 0);
pixels.show();
Outs = 0;
Strikes = 0;
irrecv.resume();
break;
//********************************************************************
//Case 7 Inning -Decreases Inning count updates display
case 185://185 is the low byte value from IR xmiter when number three button is pressed
if ((Inning > 0) && (atBat == 1))
{
atBat = 0;
display1.updateDigit ((Inning + 8), (hScore[Inning]) , 0, (hColor ) , 0 );
display1.show();
}
else
{
atBat = 1;
display1.updateDigit ((Inning ), (gScore[Inning]) , (gColor ), 0 , 0 );
display1.show();
Inning = (Inning - 1);
}
if (Inning <= 1)
{
(Inning = 1);
}
//turns off all Strike LEDs
//****************************************
pixels.setPixelColor(0, 0, 0, 0);
pixels.setPixelColor(1, 0, 0, 0);
pixels.setPixelColor(2, 0, 0, 0);
//***************************************
//turns off all out LEDs
pixels.setPixelColor(3, 0, 0, 0);
pixels.setPixelColor(4, 0, 0, 0);
pixels.setPixelColor(5, 0, 0, 0);
//********************************************
//clears base runner LEDs
pixels.setPixelColor(8, 0, 0, 0);
pixels.setPixelColor(9, 0, 0, 0);
pixels.setPixelColor(10, 0, 0, 0);
pixels.show();
Outs = 0;
Strikes = 0;
irrecv.resume();
break;
//********************************************************************************
//remove this and add switch on score board to protect aginist accedental resets
//Case 7 reset everything
case 37:// 37 is the low byte value from IR xmiter when CC button is pressed
//use arrars to do this
//Set all Score Digits to 0 and dim
display1.updateDigit (1, 0 , 20, 0, 0);
display1.updateDigit (2, 0 , 20, 0, 0);
display1.updateDigit (3, 0 , 20, 0, 0);
display1.updateDigit (4, 0 , 0 , 20, 0);
display1.updateDigit (5, 0 , 0, 20, 0);
display1.updateDigit (6, 0 , 0, 20, 0);
display1.updateDigit (7, 0 , 20, 0, 0);
display1.updateDigit (8, 0 , 20, 0, 0);
display1.updateDigit (9, 0 , 20, 0, 0);
display1.updateDigit (10, 0 , 20, 0, 0);
display1.updateDigit (11, 0 , 20, 0, 0);
display1.updateDigit (12, 0 , 20, 0, 0);
display1.updateDigit (13, 0 , 20, 0, 0);
display1.updateDigit (14, 0 , 20, 0, 0);
display1.updateDigit (15, 0 , 0, 20, 0);
display1.updateDigit (16, 0 , 0, 20, 0);
display1.show();
//Reset Strikes, outs and on Base
pixels.setPixelColor(0, 0, 0, 0);
pixels.setPixelColor(1, 0, 0, 0);
pixels.setPixelColor(2, 0, 0, 0);
pixels.setPixelColor(3, 0, 0, 0);
pixels.setPixelColor(4, 0, 0, 0);
pixels.setPixelColor(5, 0, 0, 0);
pixels.setPixelColor(6, 0, 0, 0);
pixels.setPixelColor(7, 0, 0, 0);
pixels.setPixelColor(8, 0, 0, 0);
pixels.show();
//Set all score to zero
gScore[1] = 0;
hScore[1] = 0;
gScore[2] = 0;
hScore[2] = 0;
gScore[3] = 0;
hScore[3] = 0;
gScore[4] = 0;
hScore[4] = 0;
gScore[5] = 0;
hScore[5] = 0;
gScore[6] = 0;
hScore[6] = 0;
atBat = 0;
Inning = 1;
Outs = 0;
Strikes = 0;
// Serial.print(Inning);
// Serial.print(atBat);
// Serial.print(Inning);
irrecv.resume();
break;
case 89: //man on 1st Right Arrow Button pressed
if (On1st == 0)
{
On1st = 1;
pixels.setPixelColor(10, runrG, runrR, runrB);
// reset strike count
Strikes = 0;
pixels.setPixelColor(0, 0, 0, 0);
pixels.setPixelColor(1, 0, 0, 0);
pixels.setPixelColor(2, 0, 0, 0);
pixels.show();
}
else
{
On1st = 0;
pixels.setPixelColor(10, 0, 0, 0);
pixels.show();
}
irrecv.resume();
break;
case 129: //man on second Up arrow pressed
if (On2nd == 0)
{
On2nd = 1;
//Set strikes to zero if on base
Strikes = 0;
pixels.setPixelColor(0, 0, 0, 0);
pixels.setPixelColor(1, 0, 0, 0);
pixels.setPixelColor(2, 0, 0, 0);
pixels.show();
pixels.setPixelColor(9, runrG, runrR, runrB);
pixels.show();
}
else
{
On2nd = 0;
pixels.setPixelColor(9, 0, 0, 0);
pixels.show();
}
irrecv.resume();
break;
case 168: // man om 3rd Left arrow button pressed
if (On3rd == 0)
{
On3rd = 1;
//Set strikes to zero if on base
Strikes = 0;
pixels.setPixelColor(0, 0, 0, 0);
pixels.setPixelColor(1, 0, 0, 0);
pixels.setPixelColor(2, 0, 0, 0);
pixels.setPixelColor(8, runrG, runrR, runrB);
pixels.show();
}
else
{
On3rd = 0;
pixels.setPixelColor(8, 0, 0, 0);
pixels.show();
}
irrecv.resume();
break;
}//switch loByte
//*****************************************************************
//restarts IR recive data
irrecv.resume();
}//while ir
}//Check IR




Awesome work!!
Nice to see how this works besides its complexity, great job!!!