Using SIPO shift registers to reduce pin usage on an Arduino

I have made a nifty "drag racing christmas tree" using 2 SIPO shift registers (connected back to back) to allow independant comtrol of all 14 LEDs ( 2xred 2xGreen 6xyellow and 4x blue - the staging and prestaging lights).

And for the LED control this only uses three pins (latch clock and data) on the Arduino.

I'll try make a video of this in action this weekend but here is the code


const unsigned int l_a1=11;
const unsigned int l_a2=8;
const unsigned int l_a3=9;
const unsigned int l_g=10;
const unsigned int l_r=14;
const unsigned int r_a1=3;
const unsigned int r_a2=0;
const unsigned int r_a3=1;
const unsigned int r_g=2;
const unsigned int r_r=6;
const unsigned int sw_l=3;
const unsigned int sw_r=2;
const unsigned int lst=12;
const unsigned int lps=13;
const unsigned int rst=4;
const unsigned int rps=5;

unsigned int reg_val=0;
unsigned int rndnum;
const int sr_latch = 11;
const int sr_clock = 10;
const int sr_data = 12;

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
Adafruit_SSD1306 display(15);

int count=0;
int finish=0;
int loffset=0;
int roffset=0;
int lelap;
int relap;
int lfinish;
int rfinish;
int swl;
int swr;
int repeat;
int lron=0;
int rron=0;

unsigned long msec, msecstart, ltime=0, rtime=0, lgtime, rgtime;
String stageing = String("no");

int l_staged=0;
int r_staged=0;
String tree_type = String("full");
// String tree_type = String("pro");
// int pro4=0;
int pro4=1;

void setup(){

pinMode(sr_data, OUTPUT);
pinMode(sr_clock, OUTPUT);
pinMode(sr_latch, OUTPUT);

Serial.begin(9600);

pinMode(sw_l, INPUT_PULLUP);
pinMode(sw_r, INPUT_PULLUP);
pinMode(A0, OUTPUT); // ????? needed???

writereg(65535);
delay(3000);

// get stageeing value
writereg(0);
bitWrite(reg_val, lps, HIGH);
writereg(reg_val);
msec=millis();
msecstart=msec;
dispint(count, 0x3d);
dispint(count, 0x3c);
while (msec - msecstart < 3000 ) { // 3 sec to stop stageing
if ( digitalRead(sw_l) == 0 ) {
count++;
// Serial.println(count);
dispint(count, 0x3c);
dispint(count, 0x3d);
delay(300);
msecstart=msec; }
else { msec=millis();}

}

if (count == 0) { stageing="yes"; }
else { stageing="no";
bitWrite(reg_val, rps, HIGH); }

writereg(reg_val);
delay(2000); // could put random delay here
count=0;
reg_val=0;
writereg(reg_val);

// get tree type
msec=millis();
msecstart=msec;
dispint(count, 0x3d);
dispint(count, 0x3c);
while (msec - msecstart < 3000 ) { // 3 sec to switch select tree
rndnum=random(65535);
writereg(rndnum);
if ( digitalRead(sw_l) == 0 ) {
count++;
// Serial.println(count);
dispint(count, 0x3c);
dispint(count, 0x3d);
delay(300);
msecstart=msec; }
else { msec=millis();}

}

if (count == 0) { tree_type = "full"; }
if (count == 1) { tree_type = "pro";
pro4=1; }
if (count == 2) { tree_type = "pro";
pro4=0; }
if (count > 2 ) {tree_type = "greens"; }
writereg(0);
delay(2000); // could put random delay here
}

void(* resetFunc) (void) = 0;//declare reset function at address 0

void loop() {
lgtime=30000;
rgtime=30000;
lfinish=0;
rfinish=0;

dispint(0, 0x3c);
dispint(0, 0x3d);
if (stageing == "yes" ) {
bitWrite(reg_val, lps, HIGH);
bitWrite(reg_val, rps, HIGH);
writereg(reg_val);
}
//stage players here
if (stageing == "yes" ) {
l_staged=0;
r_staged=0;
while ( (l_staged == 0 ) || ( r_staged == 0 ) ) {
swl=digitalRead(sw_l);
if (swl==0) {
l_staged=1;
bitWrite(reg_val, lst, HIGH);
writereg(reg_val);
}
swr=digitalRead(sw_r);
if (swr==0) {
r_staged=1;
bitWrite(reg_val, rst, HIGH);
writereg(reg_val);
}
}
}

rndnum=random(3000);

if (tree_type == "greens") {
lgtime=millis() + rndnum + 1000 ;
rgtime=lgtime;
}
if (tree_type == "full") {
lgtime=millis() + rndnum + 1500 + loffset;
rgtime=millis() + rndnum + 1500 + roffset;
}
if (tree_type == "pro") {
lgtime=millis() + rndnum + 400 + loffset;
rgtime=millis() + rndnum + 400 + roffset;
if (pro4==0) {
lgtime+=100;
rgtime+=100;
}
}

delay(rndnum);
msecstart=millis();
// Serial.print("starting ");
// Serial.println(count);
Serial.println();
Serial.println();
Serial.println();

while ( (lfinish == 0 ) || ( rfinish == 0 ) ) {
ltime = millis() - msecstart - loffset;
rtime = millis() - msecstart - roffset;
swl=digitalRead(sw_l);

if (swl == 0 && lfinish == 0 ) {
lfinish = 1;
lelap = millis() - lgtime;
// dispint(lelap, 0x3d);
bitWrite(reg_val, l_a1, LOW);
bitWrite(reg_val, l_a2, LOW);
bitWrite(reg_val, l_a3, LOW);

if (lelap < 0) {
    bitWrite(reg_val, l_r, HIGH);
    bitWrite(reg_val, lst, LOW);
    bitWrite(reg_val, lps, LOW);       
   writereg(reg_val);
   lron=1;

// Serial.print("Left redlighted ");
// Serial.println(lelap);
}
else {
// Serial.print("Left time is ");
// Serial.println(lelap);
bitWrite(reg_val, l_g, HIGH);
bitWrite(reg_val, lst, LOW);
bitWrite(reg_val, lps, LOW);
}
}
swr=digitalRead(sw_r);

if (swr == 0 && rfinish == 0 ) {
rfinish = 1;
relap = millis() - rgtime;
// dispint(relap, 0x3c);
bitWrite(reg_val, r_a1, LOW);
bitWrite(reg_val, r_a2, LOW);
bitWrite(reg_val, r_a3, LOW);

if (relap < 0) {
   bitWrite(reg_val, r_r, HIGH);
   bitWrite(reg_val, rst, LOW);
   bitWrite(reg_val, rps, LOW);        
   writereg(reg_val);
   rron=1;  

// Serial.print("Right redlighted ");
// Serial.println(relap);
}
else {
// Serial.print("Right time is ");
// Serial.println(relap);
bitWrite(reg_val, r_g, HIGH);
bitWrite(reg_val, rst, LOW);
bitWrite(reg_val, rps, LOW);
}
}
writereg(reg_val);

if (tree_type == "full") {
if ( lfinish == 0 ) {
if ( ltime > 1500 ) {
if (lron==0 ){ bitWrite(reg_val, l_g, HIGH); }
if (lron==0 ){ bitWrite(reg_val, l_a3, LOW);}
}
else {
if (ltime > 1000) {
if (lron==0 && lfinish ==0) { bitWrite(reg_val, l_a3, HIGH);}
if (lron==0){ bitWrite(reg_val, l_a2, LOW); }}
else {
if (ltime > 500) {
if (lron==0) { bitWrite(reg_val, l_a2, HIGH);}
if (lron==0) { bitWrite(reg_val, l_a1, LOW); }}
else {
if (ltime >= 0 ) {
if (lron==0) { bitWrite(reg_val, l_a1, HIGH);}
}
}
}
}
writereg(reg_val);
}
if (rfinish == 0 ) {
if ( rtime > 1500 ) {
if (rron==0 ){ bitWrite(reg_val, r_g, HIGH);}
if (rron==0){ bitWrite(reg_val, r_a3, LOW);}
}
else {
if (rtime > 1000) {
if (rron==0 && rfinish ==0){ bitWrite(reg_val, r_a3, HIGH);}
if (rron==0){ bitWrite(reg_val, r_a2, LOW); }}
else {
if (rtime > 500) {
if (rron==0){ bitWrite(reg_val, r_a2, HIGH);}
if (rron==0){ bitWrite(reg_val, r_a1, LOW); }}
else {
if (rtime >= 0 ) {
if (rron==0){ bitWrite(reg_val, r_a1, HIGH);}
}
}
}
}
writereg(reg_val);
}
}

if (tree_type == "greens") {

if ( millis() >= lgtime) {
if (lron==0) { bitWrite(reg_val, l_g, HIGH);}
if (rron==0){ bitWrite(reg_val, r_g, HIGH);}
writereg(reg_val);
}
}

if (tree_type == "pro") {
if ( lfinish == 0 ) {
if (ltime + (pro4 * 100) > 500 ) {
if (lron==0){ bitWrite(reg_val, l_a1, LOW); }
if (lron==0){ bitWrite(reg_val, l_a2, LOW);}
if (lron==0){bitWrite(reg_val, l_a3, LOW);}
if (lron==0 && lfinish ==0){ bitWrite(reg_val, l_g, HIGH); } }

else {
if (ltime > 0 ) {
if (lron==0){ bitWrite(reg_val, l_a1, HIGH); }
if (lron==0){ bitWrite(reg_val, l_a2, HIGH);}
if (lron==0){ bitWrite(reg_val, l_a3, HIGH);} }

   }

writereg(reg_val);
}
if (rfinish == 0 ) {
if (rtime + (pro4 * 100) > 500 ) {
if (rron==0){ bitWrite(reg_val, r_a1, LOW); }
if (rron==0){ bitWrite(reg_val, r_a2, LOW);}
if (rron==0){ bitWrite(reg_val, r_a3, LOW);}
if (rron==0 && rfinish ==0){ bitWrite(reg_val, r_g, HIGH); }}

else {
if (rtime > 0 ) {
if (rron==0){ bitWrite(reg_val, r_a1, HIGH); }
if (rron==0){ bitWrite(reg_val, r_a2, HIGH);}
if (rron==0){ bitWrite(reg_val, r_a3, HIGH); }}

   }

writereg(reg_val);
}
}

}
bitWrite(reg_val, lps, LOW);
bitWrite(reg_val, rps, LOW);
writereg(reg_val);
dispint(lelap, 0x3d);
dispint(relap, 0x3c);
// light up winning lane

if (lelap > 0 ) {
if (relap < 0 || lelap <= relap) {
bitWrite(reg_val, lps, HIGH);
}
}

if (relap > 0 ) {
if (lelap < 0 || relap <= lelap) {
bitWrite(reg_val, rps, HIGH);
}
}
writereg(reg_val);

if (lelap < 0 && relap < 0) {

for (int i = 0; i < 10; i++) {
     bitWrite(reg_val, lps, HIGH);
	 bitWrite(reg_val, rps, HIGH); 
     writereg(reg_val);	 
     delay(300);
     bitWrite(reg_val, lps, LOW);
	 bitWrite(reg_val, rps, LOW);
     writereg(reg_val);		 
     delay(300);

}
}

delay(2000);

//Serial.println("1111111 ");
repeat=0;
while (repeat==0) {
swl=digitalRead(sw_l);
if (swl==0) { repeat=1;}
swr=digitalRead(sw_r);
if (swr==0) { resetFunc(); }//call reset
}
//Serial.println("222222 ");
repeat=0 ;
lfinish=0;
rfinish=0;
swl=1;
swr=1;
lron=0;
rron=0;
reg_val=0;
writereg(reg_val);

delay(2000);
}
void dispint(int dint, char i2caddr) {
display.setTextSize(3);
display.setTextColor(WHITE);
display.begin(SSD1306_SWITCHCAPVCC, i2caddr);
display.clearDisplay();
display.drawRoundRect(0, 0, 127, 32, 8, WHITE);
display.setCursor(20, 4);
display.print(dint);
display.display();
}

void writereg(unsigned int val) {
digitalWrite(sr_latch, LOW);
for (int i = 0; i < 16; i++) {
boolean j = val%2;
val = val/2;
// Serial.print(i);
// Serial.print(" ");
// Serial.println(j);
digitalWrite(sr_clock, LOW);
digitalWrite(sr_data, j);
digitalWrite(sr_clock, HIGH);
}
digitalWrite(sr_latch, LOW);
digitalWrite(sr_latch, HIGH);
}

Welcome to the forum

Please follow the advice given in the link below when posting code, in particular the section entitled 'Posting code and common code problems'

Use code tags (the < CODE/ > icon above the compose window) to make it easier to read and copy for examination

https://forum.arduino.cc/t/how-to-get-the-best-out-of-this-forum

Please post your full sketch, using code tags when you do

Posting your code using code tags prevents parts of it being interpreted as HTML coding and makes it easier to copy for examination

In my experience the easiest way to tidy up the code and add the code tags is as follows

Start by tidying up your code by using Tools/Auto Format in the IDE to make it easier to read. Then use Edit/Copy for Forum and paste what was copied in a new reply. Code tags will have been added to the code to make it easy to read in the forum thus making it easier to provide help.

It is also helpful to post error messages in code tags as it makes it easier to scroll through them and copy them for examination

Please present code only properly formatted and in code tags.

1 Like

A schematic would also be nice.

1 Like

I'll work on these two things on the weekend. Is there software available to help draw the schematics?

Do not use Fritzing!
Do not use PCB software for now.

Hand drawn circuits are welcome. Keep Vcc on top, GND on bottom. Arrange symbolic (rectangle) modules for signal flow from left (buttons, sensors...) to right (drivers to motors...). Omit unused pins. Add type names to all modules and labels (pin names) to all signal lines.

Use Paint or simply (squared) paper and pencil and take a photo.

Have a read of this:

1 Like
  • Welcome to the group.

  • In the Arduino IDE, use Ctrl T or CMD T to format your code then copy the complete sketch. Use the < CODE / > icon from the ‘posting menu’ to attach the copied sketch.

  • Add meaningful comments to your code.

  • Avoid using delay(...) as it blocks code execution for the delay interval.

EDIT: All my observations below would be meaningful if you asked for help. I didn't notice it was in the showcase category.

As you don't display the string, you can use enum type instead:

enum : uint8_t {full, pro, greens} tree_type = full;
...

...
if (tree_type == greens) {
  ...
}

Same thing for stageing and any other status byte.

You can use SPI.transfer16(val16) from SPI library as well:
writereg(reg_val); becomes SPI.transfer16(reg_val); EDIT: CS (or sr_latch in your case) should be managed independently though...

I didn't investigate any further (513 lines) but your variables name could be more explicit:

  swl = 1;
  swr = 1;
  lron = 0;
  rron = 0;

l and r probably stand for left and right, sw for switch maybe but it's not obvious...

Some (many) people use snake_case or camelCase to help reading:

displayInt(count, 0x3c);

while() and delay() are blocking instructions and should be avoided to keep the program reactive to events. Use if() and millis() instead. See blink without delay and state machine (can't remember the nice example I once found) on how to do that...

See post #7
If you find Fritzing easy to understand that is OK, just make sure the breadboard view is easy to follow.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.