4x 7 Segment LED Scoreboard Programming with Arduino Nano and 4x 74HC595's

Hi Guys, new to Arduino but I’ve built an LED Strip Light Scoreboard (4x Digits/7 Segments per digit).

Each 7 Segment Digit is connected to a 74HC595 shift register, in total I have 4 shift registers which are daisy chained and connected to an Arduino Nano.

Having some struggles programming the shift registers, I’ve managed to get a test code uploaded which is cycling through each segment individually (which is great)… but now I need to be able to program each of the numbers using both an IR Remote and also “UP/DOWN” buttons for each side of the scoring.

I’ve been looking at a few projects people have done previously but their coding is not using a Arduino Nano and not really using shift registers how they can be used (daisychained).

I’ve uploaded the current sketch I am working with…

Would really appreciate any help that you guys can provide to get this all working :slight_smile:

Cheers!
Brendo

scoreboard_-_current.zip (4 KB)

Nobody is going to open a .zip file. Please post it inline with code tags, or if it's rejected due to excessive size, add it to your post as an attachment.

Good point… sorry!! I’ve attached the .ino file - hopefully that is ok?

The code itself is way bigger than the maximum allowed 9000 characters for a post.

scoreboard_-_current.ino (26.3 KB)

becauseracecar:
Good point.. sorry!! I've attached the .ino file - hopefully that is ok?

The code itself is way bigger than the maximum allowed 9000 characters for a post.

It's fine for the forum. The code would not be so big if you coded your humongous switch-case statements as arrays instead.

Edit - wait, I see... you have no segment selection logic at all. Every digit is hard coded repeatedly over and over. Not great, but if it currently works it is only a waste of processor resources.

// Visitor Count Switch-case, displays Visitor score from 0-99
    switch (visitorCount){
    case 0:
      visitorTensDisplay = 0;
      visitorOnesDisplay = 63;
      break;
    case 1:
      visitorTensDisplay = 0;
      visitorOnesDisplay = 6;      
      break;
    case 2:

Are you asking us how to add buttons and IR capability? Have you researched and written test sketches for those, separate from this one, for testing? If not, you have come for help too soon. We can't be expected to solve a problem that doesn't exist yet! The IDE ships with examples for buttons, and the IR library has examples for IR decoding. Those should give you a good idea of where to start.

100% open to better ways of writing it, as mentioned in the original post.. alot of it was derived from a different code someone else had created and then I've tried to adjust it to make it work for how my setup is put together.

becauseracecar:
100% open to better ways of writing it, as mentioned in the original post

Yes, but what has your research turned up? Are there specific problems that we can help you with? Actually, there is a lot of online material about IR and button implementations. It's not fair to ask here before you've been there.

The bottom line is, we want to see an actual program that includes buttons and IR even if it doesn't work. Only then can we see directly what problems you face.

Maybe you can get some ideas here on how to shorten the code

/*
6-digit score board
 
 Pin 3 = right score input (active LOW)
 Pin 2 = left score input (active LOW)
 Pin 4 = reset to zero on display (active LOW)
 Pin 5 = start (active LOW)
 
 Register 1 (closest to Atmega) = left score 100's = Left 1
 Register 2 = left score 10's                      = Left 2
 Register 3 = left score 1's                       = Left 3
 Register 4 = right score 100's                    = Right 1
 Register 5 = right score 10's                     = Right 2
 Register 6 = right score 1's                      = Right 3
 
 Power on would set all digits to zero. Score would count up to 999 and stay there until reset, even if additional score input is received.
 */
#include <SPI.h>
unsigned long debounceTime = 200;

// declare pins used
byte rightScorepin = 3;
byte leftScorepin =2;
byte resetScorepin = 4;
byte startpin = 5;
byte latch = 10; 
byte dim = 9;
byte activityLed = 8;

// declare variables
byte leftScoreup = 0;
byte rightScoreup = 0;
unsigned long leftscoreTime = 0;
unsigned long rightscoreTime = 0;

byte clearScore = 0;
byte scoreChange = 0;

byte leftscore1s = 0;
byte leftscore10s = 0;
byte leftscore100s = 0;
byte rightscore1s = 0;
byte rightscore10s =0;
byte rightscore100s = 0;

// declare time variables, read switches every 5mS
unsigned long timeNow = 0;
unsigned long timePrior =0;
unsigned long timePeriod = 5;

// # to segment mapping, 1 turns a segment on
byte displayArray[10] = {
0b00111111, // 0  D-g-f-e-d-c-b-a
0b00000110, // 1     a
0b01011011, // 2  f     b
0b01001111, // 3     g
0b01100110, // 4  e     c
0b01101101, // 5     d        DP
0b01111101, // 6
0b00000111, // 7
0b01111111, // 8
0b01101111, // 9
};
void setup(){
  Serial.begin(9600);
  // setup switch pins with internal pullups
  pinMode (rightScorepin, INPUT);
  pinMode (leftScorepin, INPUT);
  pinMode (resetScorepin, INPUT);
  pinMode (startpin, INPUT);
  digitalWrite (rightScorepin, HIGH);
  digitalWrite (leftScorepin, HIGH);
  digitalWrite (resetScorepin, HIGH);
  digitalWrite (startpin, HIGH);

  // latch pin for SPI transfers
  pinMode (latch, OUTPUT);
  digitalWrite (latch, HIGH);

  // output enable for shift registers
  pinMode (dim, OUTPUT);
  digitalWrite (dim, LOW);

  // activity LED, flash when good switch press seen
  pinMode (activityLed, OUTPUT);
  digitalWrite (activityLed, LOW);

  SPI.begin();

  // clear the display
  digitalWrite (latch, LOW);
  SPI.transfer (0);
  SPI.transfer (0);
  SPI.transfer (0);
  SPI.transfer (0);
  SPI.transfer (0);
  SPI.transfer (0);
  digitalWrite (latch, HIGH);
}

void loop(){
  timeNow = millis();
  if ((timeNow - timePrior) >=timePeriod){  // read the buttons every 5mS
    timePrior = timeNow;
    digitalWrite (activityLed, LOW); // turn off LED if anything turned it on
    
    if ((digitalRead (leftScorepin) == 0) && (timeNow - leftscoreTime >=debounceTime)){
        leftScoreup = 1;  // set flag to show button was active
        Serial.println("left score ");
        leftscoreTime = millis(); // set flag to show state
        digitalWrite (activityLed, HIGH); // light up for good score
    }
    
    if ((digitalRead (rightScorepin) == 0) && (timeNow - rightscoreTime >=debounceTime)){
        rightScoreup = 1; // set flag to show button was active
        Serial.println("right score ");
        rightscoreTime = millis(); // set flag to show state
        digitalWrite (activityLed, HIGH);  // ligth up for goods score
    }

    if (digitalRead (resetScorepin) == 0){
      digitalWrite (activityLed, HIGH); // light up for reset
      leftscore1s = 0;
      leftscore10s = 0;
      leftscore100s = 0;
      rightscore1s = 0;
      rightscore10s = 0;
      rightscore100s = 0;
      clearScore = 0; // clear flag for next pass
      Serial.println ("cleared ");
      scoreChange = 1; // flag to show score has changed
    } // end score reset
    // were any buttons set?

    if  ((leftScoreup == 1) && ((leftscore100s * 100) + (leftscore10s *10) + leftscore1s) <999){  // left score & not reached 999
      scoreChange = 1;  // flag to show score has changed
      leftScoreup = 0; // clear flag for next pass
      leftscore1s = leftscore1s + 1; // increment score
      if (leftscore1s == 10){  // rollover 1s, increment 10s
        leftscore1s = 0;
        leftscore10s = leftscore10s +1;
        if (leftscore10s == 10){ // rollover 10s, increment 100s
          leftscore10s = 0;
          leftscore100s = leftscore100s+1;
        }
      }
      Serial.print ("left = ");
      Serial.print (leftscore100s);
      Serial.print (",");
      Serial.print (leftscore10s);
      Serial.print (",");
      Serial.println (leftscore1s);
    } // end left score increase

    if  ((rightScoreup == 1) && ((rightscore100s * 100) + (rightscore10s *10) + rightscore1s) <999){  // right score & not reached 999
      scoreChange = 1; // flag to show score has changed
      rightScoreup = 0; // clear flag for next pass
      rightscore1s = rightscore1s + 1; // increment score
      if (rightscore1s == 10){ // rollover 1s, increment 10s
        rightscore1s = 0;
        rightscore10s = rightscore10s +1;
        if (rightscore10s == 10){ // rollover 10s, increment 100s
          rightscore10s = 0;
          rightscore100s = rightscore100s+1;
        }
      }
      Serial.print ("right = ");
      Serial.print (rightscore100s);
      Serial.print (",");
      Serial.print (rightscore10s);
      Serial.print (",");
      Serial.println (rightscore1s);
    } // end right score increase

    if (scoreChange ==1){ // did any score change?  Send the updated digits out
      scoreChange = 0; // clear flag for next pass
      digitalWrite (latch, LOW);
      SPI.transfer (displayArray[rightscore1s]);
      SPI.transfer (displayArray[rightscore10s]);
      SPI.transfer (displayArray[rightscore100s]);
      SPI.transfer (displayArray[leftscore1s]);
      SPI.transfer (displayArray[leftscore10s]);
      SPI.transfer (displayArray[leftscore100s]);
      digitalWrite (latch, HIGH);
      Serial.println("score updated ");
    } // end score update
  } // end time check
} // end loop

At the stage I’m at with it, shortening it isn’t going to get it working unless the core functionality is in the current code which I don’t beleive it is as even with the IR remote keys mapped, nothing is happening when a corresponding key is pressed. So the code as attached is what I need assistance with to at least get that working firstly.

You should walk yourself back through the logic that you used to build the constants you used like

     visitorOnesDisplay = 109;

and then write code to force the computer to do the same work. You clearly hand coded each of 100 cases, did you not observe any patterns that you could follow? The computer should just follow those patterns.

Failing that, you should at least get rid of the “magic numbers” by giving them meaningful names, like:

const byte DIGIT_9 = 109;
...
      visitorOnesDisplay = DIGIT_9;

becauseracecar:
At the stage I'm at with it, shortening it isn't going to get it working unless the core functionality is in the current code which I don't beleive it is as even with the IR remote keys mapped, nothing is happening when a corresponding key is pressed. So the code as attached is what I need assistance with to at least get that working firstly.

Sorry, I see the IR code now. Part of the problem with such a huge sketch...

In that case you need to tell us the difference between what you want/expect it to do, and what it is currently doing/not doing. For example you made no mention of the button switch functionality in your reply.

But I will ask you again, have you tested the buttons and IR separately from this program?

To be fair the mapping of each digit case was derived from another code.

It’s currently doing nothing :slight_smile: Press the “up” key on the remote as programmed to the below results in no change…

case 0xFF629D: // UP (increases the visitor count by one)
visitorCount = visitorCount+1;
if(visitorCount>99){ // after 99 runs, counter goes back to 0
visitorCount = 0;
}
Serial.println(“CH”);

No digits are illuminated on any of the 4 segments. But watching the serial monitor I can see that the command 0xFF629D is being received from the IR Remote.

The same results for all other buttons that are mapped when pressed on the remote… no change to any of the segments.

The only time I have seen any segments actually work is using the below sample code to test the daisy chained shift registers:

int latchPin = 3;
int dataPin = 2;
int clockPin = 4;
const float segSpeed = .05; //How quickly to flash each led segment.
const int digLoop = 100; //How many times to repeat the segment sequence.
const int nextPin = 200; //Delay before moving to the next digit on the display.
const int repeatSeq = 300; //Delay before starting over from the first digit.
const int pinArray[4] = {3, 9, 10, 11}; //Defines PWM pins, the pins that activate each digiti.
const int digArray[11][9] = {
{127, 191, 223, 239, 247, 251, 255, 254, 255}, //0: 127 top led segment
{255, 191, 223, 255, 255, 255, 255, 254, 255}, //1: 191 top right seg
{127, 191, 255, 239, 247, 255, 253, 254, 255}, //2: 255 is NO segment
{127, 191, 223, 239, 255, 255, 253, 254, 255}, //3: 223 bottom right seg
{255, 191, 223, 255, 255, 251, 253, 254, 255}, //4:
{127, 255, 223, 239, 255, 251, 253, 254, 255}, //5: 239 bottom seg
{127, 191, 223, 239, 247, 255, 253, 254, 255}, //6: 247 bottom left seg
{127, 191, 223, 255, 255, 255, 255, 254, 255}, //7:
/*Example for the number 7:

  • 127: |_ON |
  • 251:OFF ON:191
  • 253: OFF | |
  • 247:OFF ON:223
  • 239: OFF |_|
    */
    {127, 191, 223, 239, 247, 251, 253, 254, 255}, //8: 251 top left seg
    {127, 191, 223, 239, 255, 251, 253, 254, 255}, //9: 253 center seg
    {255, 255, 255, 255, 255, 255, 255, 254, 255}, //DP: Decimal Point seg
    };
    const int arrHeight = 9; // (0-9) 9 displays digits 0-9.
    const int arrWidth = 6; // (0-8) how many segments from each row to use. 7 will add the decimal row.

void setup() {
//Serial.begin(9600); //which port to output serial display data. unused. slows display down.
pinMode(latchPin, OUTPUT);
pinMode(dataPin, OUTPUT);
pinMode(clockPin, OUTPUT);
}

void loop() {
for (int i = 0; i <= 3; i++) { //cycles through all PWM pins
for (int k = 0; k <= arrHeight; k++) { //cycles through countTo number of digArray rows
analogWrite((pinArray*), 255); //turns PWM pin(i) on*

  • for (int h = 0; h <= digLoop; h++) { //how many times to cycle through columns*
  • for (int j = 0; j <= arrWidth; j++) { //cycles through all displayed column data*
  • digitalWrite(latchPin, HIGH);*
  • shiftOut(dataPin, clockPin, MSBFIRST, digArray[k][j]);*
  • digitalWrite(latchPin, LOW);*
  • delay(segSpeed); //delay until next segment displayed sequence*
  • }*
  • }*
    _ analogWrite((pinArray*), 0); //turns PWM pin(i) off*_
    * delay(nextPin); //delay until next displayed digit sequence*
    * }*
    * }*
    * delay(repeatSeq); //delay until everything is started over again*
    }

I haven't been able to test anything with the buttons other than looking at the serial monitor and seeing values being populated. If there is a sketch anyone can recommend to test functionality with the remote that would be awesome also

Oh, so the buttons you are referring to are only on the IR remote? Confused now.

So there are two methods to carry out the same function.

The IR Remote, and also physical push buttons. In the current sketch I have not called out the physical buttons but I have called out the IR Remote buttons.

If I can get the IR remote key presses functioning then I should be able to work out what's required to get the physical push buttons working also.

becauseracecar:
I haven't been able to test anything with the buttons other than looking at the serial monitor and seeing values being populated. If there is a sketch anyone can recommend to test functionality with the remote that would be awesome also

Delightfully ambiguous. Did you run any example sketch that came with the IR library, and if so, what was the result?

Dude chill out a bit, as mentioned I haven't programmed before this is literally a first attempt.

I have about 4 different IR libraries in the IDE Desktop Application, I haven't tested the IR Remote sketches as yet no, the test codes for IR Remote I did look at just seem to test the send/receive functionality from with the IR Remote. I can see the Arduino is receiving the commands from my IR Remote through the Serial Monitor so that is not a problem, but clearly something in my scoreboard sketch with how I have mapped the IR button press command to a function (such as increment a number up or down) is an issue.

I think the issues with my sketch extend past just the remote functionality though, if there is anymore info needed to validate that all the other info in the sketch (aside from the IR Remote cases) is correct with exactly how I have everything physically setup and connected I'm more than happy to provided that.