question: shifting 32 leds with four 74hc595's

Hi all,

I’m working on a school project, and kinda stuck at the moment, and looking for advice. My skill-level is moderate, but I’m really trying to learn.

I want to use a push button as an input; set it high and have the loop (?) start.

After setting the push button high, I want to use two 74hc595’s to shift 2 x 8 leds in a chase pattern for an x amount of time. Simultaneously (while the other 2 595’s are in a chase pattern loop) i want to shift another 74hc595 to shift 8 leds, with a different delay.

after this sequence I want to turn the last (the 4th) 74hc595 to on for all outputs for an x amount of time.

the values for int ledOn1 seemed to have done the trick for the chase pattern, but how can i run the two 74hc595’s at the same time, and shift it to the last 74hc595, and have it stop after that? Possibly even so that once the pushbutton is set to high, that it can only be set to high once the loop has finished?

Code i have so far

//Input 74HC595 on Arduino Uno
int dataPin = 8;        //data      pin14 of 74hc595
int latchPin = 9;       //latch     pin12 of 74hc595
int clockPin = 10;      //clock     pin11 of 74hc595

// Input push button
int inPin = 7;          // input push button
int val = 0;            // variable for reading the pin status

// Input data for ledOn. Second shift register will be written first.
int ledOn2[]={1,2,4,8,16,32,64,128,1,2,4,8,16,32,64,128, 1,2,4,8,16,32,64,128,1,2,4,8,16,32,64};
int ledOn1[]={128,1,2,4,8,16,32,64,128,1,2,4,8,16,32,64,128,1,2,4,8,16,32,64,128, 1,2,4,8,16,32};


void setup() 
{
pinMode(latchPin, OUTPUT); // set pin as output
pinMode(clockPin, OUTPUT); // set pin as output
pinMode(dataPin, OUTPUT);  // set pin as output
pinMode(inPin, INPUT);     // set push button pin as intput
}

void loop() 

{
  for (int i = 0; i < 31; i++) 
  {
   
    digitalWrite(latchPin, LOW);
    shiftOut(ledOn2[i]);
    shiftOut(ledOn1[i]);
    digitalWrite(latchPin, HIGH);
    delay(90); 
  }
}

void shiftOut(byte dataOut) 
{
  boolean pinState;
  digitalWrite(dataPin, LOW);
  digitalWrite(clockPin, LOW);
  for (int i=0; i<=7; i++) 
  {
    
    digitalWrite(clockPin, LOW);
    if ( dataOut & (1<<i) ) 
      {
        pinState = HIGH;
      }
    else 
      {
        pinState = LOW;
      }
  digitalWrite(dataPin, pinState);  
  digitalWrite(clockPin, HIGH);    
  }
digitalWrite(clockPin, LOW);
}

First of all get rid of the for-loop. The loop() function is already called repeatedly.

The below should do the same as what you currently do.

void loop()
{
  // variables that indicate which element in ledOn1 and ledOn2 must be used
  static int currentLed1 = 0;
  static int currentLed2 = 0;

  digitalWrite(latchPin, LOW);
  shiftOut(ledOn2[currentLed2]);
  shiftOut(ledOn1[currentLed1]);
  digitalWrite(latchPin, HIGH);
  delay(90);

  // update the currentLed1
  currentLed1++;
  if (currentLed1 == 31)
  {
    currentLed1 = 0;
  }
  // update the currentLed2
  currentLed2++;
  if (currentLed2 == 31)
  {
    currentLed2 = 0;
  }
}

Next you can make use of individual delays; to achieve that, we use a timing based on millis(); you can have a look at the Blink-Without-Delay example that comes with the IDE. The timing will update the individual currentLedX variables.

// variable to keep track of current time
unsigned long currentTime;

// other global variables
...
...

void setup()
{
  ...
  ...
}

void loop()
{
  // variables that indicate which element in ledOn1 and ledOn2 must be used
  static int currentLed1 = 0;
  static int currentLed2 = 0;

  // variables to remember when the chaser changed a led
  static unsigned long ledDelayStarttime1;
  static unsigned long ledDelayStarttime2;

  currentTime = millis();


  digitalWrite(latchPin, LOW);
  shiftOut(ledOn2[currentLed2]);
  shiftOut(ledOn1[currentLed1]);
  digitalWrite(latchPin, HIGH);

  // if it's time to do the next pattern in ledOn1
  if (currentTime - ledDelayStarttime1 > 3000)
  {
    // update the currentLed1
    currentLed1++;
    if (currentLed1 == 31)
    {
      currentLed1 = 0;
    }
    // set a new start time for the delay
    ledDelayStarttime1 = currentTime;
  }

  // if it's time to do the next pattern in ledOn2
  if (currentTime - ledDelayStarttime2 > 5000)
  {
    // update the currentLed2
    currentLed2++;
    if (currentLed2 == 31)
    {
      currentLed2 = 0;
    }
    // set a new start time for the delay
    ledDelayStarttime2 = currentTime;
  }
}

Understand the code; it should give you the idea how to do individual timings.

Next comes a question. Why do you have 31 patterns in ledOn1 and ledOn2? You only need 16 in ledOn1 and 8 in ledOn2.

int ledOn1[] =
{
  0x0001, 0x0002, 0x0004, 0x0008,
  0x0010, 0x0020, 0x0040, 0x0080,
  0x0100, 0x0200, 0x0400, 0x0800,
  0x1000, 0x2000, 0x4000, 0x8000,
};

int ledOn2[] = 
{
  0x01, 0x02, 0x04, 0x08,
  0x10, 0x20, 0x40, 0x80,
};

Note that ledOn1 has changed; the first element is no longer a high number.
As you no longer have 31 patterns, you need to change your loop code.

Change

    currentLed1++;
    if (currentLed1 == 31)

to

    currentLed1++;
    if (currentLed1 == sizeof(ledOn1) / sizeof(ledOn1[0]))

And

    currentLed2++;
    if (currentLed2 == 31)

to

    currentLed2++;
    if (currentLed2 == sizeof(ledOn2) / sizeof(ledOn2[0]))

The sizeof operator returns the number of bytes that is used by a variable. As an int is two bytes, the sizeof(ledOn1) will be 16x2 = 32 bytes. Dividing by the size of the first element (an int so two bytes) will give you the number of elements in ledOn1. Same for ledOn2 (although it obviously gives a different number).

The advantage of this approach is that you can shrink or expand the arrays without having to worry about updating other parts of the code.

Next you need to modify shiftOut function so it can handle 16 bits at a time. Because you sometimes want to shift 16 bits and sometime 8 bits, we will add an argument to the function that indicates how many bits need to be shifted.

/*
  shift data pattern out
  input:
    dataOut: the pattern to be shifted
    numBits: number of bits in the pattern 
 */
void shiftOut(int dataOut, int numBits)
{
  boolean pinState;
  digitalWrite(dataPin, LOW);
  digitalWrite(clockPin, LOW);
  for (int i = 0; i < numBits; i++)
  {
    digitalWrite(clockPin, LOW);
    if ( dataOut & (1 << i) )
    {
      pinState = HIGH;
    }
    else
    {
      pinState = LOW;
    }
    digitalWrite(dataPin, pinState);
    digitalWrite(clockPin, HIGH);
  }
  digitalWrite(clockPin, LOW);
}

And the shiftOuts in loop() now change to

  shiftOut(ledOn2[currentLed2], 8);
  shiftOut(ledOn1[currentLed1], 16);

There are other options for the above that don’t require modification of shiftOut; I opted for this one.

The next problem is that you have to actually have to shift 32 bits; so you also need to shift the last 8 bits (shift register 4) into the 4 cascaded HC595s.
You can shift the last 8 bits by adding an additional shiftOut in loop()

  shiftOut(0, 8);
  shiftOut(ledOn2[currentLed2], 8);
  shiftOut(ledOn1[currentLed1], 16);

Lastly we will implement the requirement to switch the last 8 leds on when the two sequence are completed.

First add a variable to loop to hold the pattern for the last 8 bits (for shift register 4) and modify the first shiftOut statement to use this pattern.

void loop()
{
  // pattern for the last shift register; initialized with 0;
  static byte shift4pattern = 0;
  // variables that indicate which element in ledOn1 and ledOn2 must be used
  static int currentLed1 = 0;
  static int currentLed2 = 0;

  ...
  ...

  shiftOut(shift4pattern, 8);
  shiftOut(ledOn2[currentLed2], 8);
  shiftOut(ledOn1[currentLed1], 16);

  ...
  ...
}

Now instead of looping forever, we will stop the sequences for ledOn1 and ledOn2. We can achieve this by adding one extra element to ledOn1 and ledOn2 with the value 0 that we can test for.

int ledOn1[] =
{
  0x0001, 0x0002, 0x0004, 0x0008,
  0x0010, 0x0020, 0x0040, 0x0080,
  0x0100, 0x0200, 0x0400, 0x0800,
  0x1000, 0x2000, 0x4000, 0x8000,
  0
};

int ledOn2[] =
{
  0x01, 0x02, 0x04, 0x08,
  0x10, 0x20, 0x40, 0x80,
  0
};

And the checks for the time will in loop will change to

  // if it's time to do the next pattern in ledOn1 and if the pattern is finished or not
  if (currentTime - ledDelayStarttime1 > 3000 && ledOn1[currentLed1] != 0)
  {
    // update the currentLed1
    ...
    ...
  }
  // if it's time to do the next pattern in ledOn2 and if the pattern is finished or not
  if (currentTime - ledDelayStarttime2 > 2000 && ledOn2[currentLed2] != 0)
  {
    // update the currentLed2
    ...
    ...
  }

And lastly at the end of loop() you can test if both patterns are finished and set shift4pattern to 0xFF so all leds will be on.

   // test if both sequences are finished
  if (ledOn1[currentLed1] == 0 && ledOn2[currentLed2] == 0)
  {
    shift4pattern = 0xFF;
  }
} // end of loop

I leave the timing part to you for this; you should have an idea now how to approach.

We have a power failure so I leave the button part to you or somebody else.

Code compiled but not tested.

Hi sterretje, thanks so much, (dankjewel!!), for the reply.

I updated the code, but it is giving me an error: ‘too few arguments to function ‘void shiftOut(uint8_t, uint8_t, uint8_t, uint8_t)’’

Will the push button work with an if statement? if button is high, run loop.

thanks in advance

//Input 74HC595 on Arduino Uno
int dataPin = 8;        //data      pin14 of 74hc595
int latchPin = 9;       //latch     pin12 of 74hc595
int clockPin = 10;      //clock     pin11 of 74hc595

// variable to keep track of current time
unsigned long currentTime;

// Input push button
int inPin = 7;          // input push button
int val = 0;            // variable for reading the pin status

// Input data for ledOn. 
int ledOn1[] =
{
  0x0001, 0x0002, 0x0004, 0x0008,
  0x0010, 0x0020, 0x0040, 0x0080,
  0x0100, 0x0200, 0x0400, 0x0800,
  0x1000, 0x2000, 0x4000, 0x8000,
  0
};

int ledOn2[] =
{
  0x01, 0x02, 0x04, 0x08,
  0x10, 0x20, 0x40, 0x80,
  0
};


void setup() 
{
pinMode(latchPin, OUTPUT); // set pin as output
pinMode(clockPin, OUTPUT); // set pin as output
pinMode(dataPin, OUTPUT);  // set pin as output
pinMode(inPin, INPUT);     // set push button pin as intput
}


void loop()
{
  // variables that indicate which element in ledOn1 and ledOn2 must be used
  static int currentLed1 = 0;
  static int currentLed2 = 0;

  // variables to remember when the chaser changed a led
  static unsigned long ledDelayStarttime1;
  static unsigned long ledDelayStarttime2;

  currentTime = millis();

  digitalWrite(latchPin, LOW);
  shiftOut(ledOn2[currentLed2], 8);
  shiftOut(ledOn1[currentLed1], 16);
  digitalWrite(latchPin, HIGH);

  // if it's time to do the next pattern in ledOn1
  if (currentTime - ledDelayStarttime1 > 3000)
  {
    // update the currentLed1
      currentLed1++;
    if (currentLed1 == sizeof(ledOn1) / sizeof(ledOn1[0]))
    {
      currentLed1 = 0;
    }
    // set a new start time for the delay
    ledDelayStarttime1 = currentTime;
  }

  // if it's time to do the next pattern in ledOn2
  if (currentTime - ledDelayStarttime2 > 5000)
  {
    // update the currentLed2
     currentLed2++;
    if (currentLed2 == sizeof(ledOn2) / sizeof(ledOn2[0]))
    {
      currentLed2 = 0;
    }
    // set a new start time for the delay
    ledDelayStarttime2 = currentTime;
  }

  /*
  shift data pattern out
  input:
    dataOut: the pattern to be shifted
    numBits: number of bits in the pattern 
 */
void shiftOut(int dataOut, int numBits)
{
  boolean pinState;
  digitalWrite(dataPin, LOW);
  digitalWrite(clockPin, LOW);
  for (int i = 0; i < numBits; i++)
  {
    digitalWrite(clockPin, LOW);
    if ( dataOut & (1 << i) )
    {
      pinState = HIGH;
    }
    else
    {
      pinState = LOW;
    }
    digitalWrite(dataPin, pinState);
    digitalWrite(clockPin, HIGH);
  }
  digitalWrite(clockPin, LOW);
}

shiftOut(0, 8);
shiftOut(ledOn2[currentLed2], 8);
shiftOut(ledOn1[currentLed1], 16);



 // if it's time to do the next pattern in ledOn1 and if the pattern is finished or not
  if (currentTime - ledDelayStarttime1 > 3000 && ledOn1[currentLed1] != 0)
  {
    // update the currentLed1
    ...
    ...
  }
  // if it's time to do the next pattern in ledOn2 and if the pattern is finished or not
  if (currentTime - ledDelayStarttime2 > 2000 && ledOn2[currentLed2] != 0)
  {
    // update the currentLed2
    ...
    ...
  }

     // test if both sequences are finished
  if (ledOn1[currentLed1] == 0 && ledOn2[currentLed2] == 0)
  {
    shift4pattern = 0xFF;
  }
} // end of loop
}

graag gedaan :slight_smile:

You haven’t combined the pieces correctly. You’re missing a closing ‘}’ at the end of loop. And anything after the end of the shiftOut function has to be removed.

//Input 74HC595 on Arduino Uno
int dataPin = 8;        //data      pin14 of 74hc595
int latchPin = 9;       //latch     pin12 of 74hc595
int clockPin = 10;      //clock     pin11 of 74hc595

// Input push button
int inPin = 7;          // input push button
int val = 0;            // variable for reading the pin status

// Input data for ledOn. Second shift register will be written first.
//int ledOn2[] = {1, 2, 4, 8, 16, 32, 64, 128, 1, 2, 4, 8, 16, 32, 64, 128, 1, 2, 4, 8, 16, 32, 64, 128, 1, 2, 4, 8, 16, 32, 64};
//int ledOn1[] = {128, 1, 2, 4, 8, 16, 32, 64, 128, 1, 2, 4, 8, 16, 32, 64, 128, 1, 2, 4, 8, 16, 32, 64, 128, 1, 2, 4, 8, 16, 32};


int ledOn1[] =
{
  0x0001, 0x0002, 0x0004, 0x0008,
  0x0010, 0x0020, 0x0040, 0x0080,
  0x0100, 0x0200, 0x0400, 0x0800,
  0x1000, 0x2000, 0x4000, 0x8000,
  0
};

int ledOn2[] =
{
  0x01, 0x02, 0x04, 0x08,
  0x10, 0x20, 0x40, 0x80,
  0
};


void setup()
{
  pinMode(latchPin, OUTPUT); // set pin as output
  pinMode(clockPin, OUTPUT); // set pin as output
  pinMode(dataPin, OUTPUT);  // set pin as output
  pinMode(inPin, INPUT);     // set push button pin as intput

}

// variable to keep track of current time
unsigned long currentTime;

void loop()
{
  // pattern for the last shift register; initialized with 0;
  static byte shift4pattern = 0;
  // variables that indicate which element in ledOn1 and ledOn2 must be used
  static int currentLed1 = 0;
  static int currentLed2 = 0;

  // variables to remember when the chaser changed a led
  static unsigned long ledDelayStarttime1;
  static unsigned long ledDelayStarttime2;

  currentTime = millis();

  digitalWrite(latchPin, LOW);
  shiftOut(shift4pattern, 8);
  shiftOut(ledOn2[currentLed2], 8);
  shiftOut(ledOn1[currentLed1], 16);
  digitalWrite(latchPin, HIGH);

  // if it's time to do the next pattern in ledOn1 and we're not finished yet
  if (currentTime - ledDelayStarttime1 > 2000 && ledOn1[currentLed1] != 0)
  {
    // update the currentLed1
    currentLed1++;
    if (currentLed1 == sizeof(ledOn1) / sizeof(ledOn1[0]))
    {
      currentLed1 = 0;
    }
    // set a new start time for the delay
    ledDelayStarttime1 = currentTime;
  }

  // if it's time to do the next pattern in ledOn2 and we're not finished yet
  if (currentTime - ledDelayStarttime2 > 3000 && ledOn2[currentLed2] != 0)
  {
    // update the currentLed2
    currentLed2++;
    if (currentLed2 == sizeof(ledOn2) / sizeof(ledOn2[0]))
    {
      currentLed2 = 0;
    }
    // set a new start time for the delay
    ledDelayStarttime2 = currentTime;
  }

  // test if both sequences are finished
  if (ledOn1[currentLed1] == 0 && ledOn2[currentLed2] == 0)
  {
    shift4pattern = 0xFF;
  }
}

/*
  shift data pattern out
  input:
    dataOut: the pattern to be shifted
    numBits: number of bits in the pattern
*/
void shiftOut(int dataOut, int numBits)
{
  boolean pinState;
  digitalWrite(dataPin, LOW);
  digitalWrite(clockPin, LOW);
  for (int i = 0; i < numBits; i++)
  {
    digitalWrite(clockPin, LOW);
    if ( dataOut & (1 << i) )
    {
      pinState = HIGH;
    }
    else
    {
      pinState = LOW;
    }
    digitalWrite(dataPin, pinState);
    digitalWrite(clockPin, HIGH);
    Serial.print(pinState);
  }
  digitalWrite(clockPin, LOW);
}