Arduino Scrolling 56x8 LED matrix

hi! may i enquire why did you use tpic6c595 in to drive the column and 74hc595 for rows and not the other way round?

hi! may i enquire why did you use tpic6c595 in to drive the column and 74hc595 for rows and not the other way round?

My matrix is wired so the led current goes from the row to the column, and the tpic6c595 will only drain to ground, so i had to use it for the columns. You are on the right track to question my choice, it would be better to have the more powerful tpic6c595 to drive the common row and the less powerful 74hc595 for the individual columns. However, to do this, I would have to rotate my matrix (treating rows as columns and vice versa) -- not practical on my breadboard. :frowning:

thanks for clarifying =)

one more question though...

so if i decided to use only 5 matrixes

the only thing i have to modify in your code is in the Bitmap section right?

are there anything that i should lookout for?

thanks alot for the reply

Correct. The code looks at the bitmap size and deduce # of columns by assuming 8 rows per column. Good luck and have fun!

Greetings Hari,
i've made at school my own ledmatrix of 40x8. thanks for your explaining about the code and stuff.
i've made at myself a serial version of it but i got the problem that i lose the text i writen in it when i turn of my arduino. ( cause of i write into a char)
now is my question do you maybe know how i write a char into my EEPROM.

i tryed but it says i can only write a int into it so no char..
do you maybe have an idea?

Greetings,
Spikie.
btw: thanks for your great idea:)
the code of the serial part btw:

void AlphabetSoup()
{
#define INLENGTH 20        
#define INTERMINATOR 13    

char msg[INLENGTH+1];
char tmpMsg[INLENGTH+1];
int inCount;           
 for (int charIndex=0; charIndex < (sizeof(tmpMsg)-1); charIndex++)
{
  tmpMsg[charIndex] = 0;
} 
inCount = 0;
  do {
        tmpMsg[inCount] = Serial.read();    
        if (tmpMsg[inCount] == INTERMINATOR) break;
     } while (++inCount < INLENGTH);
  if (tmpMsg[0] > 30) {         
  for (int charIndex=0; charIndex < (sizeof(tmpMsg)-1); charIndex++)
  {
    msg[charIndex] = tmpMsg[charIndex];
  }  
}

// char msg[] = "[ ]^_`abcdefghijklmnopqrstuvwxyz{|} ";

spikie, that's cool that you've taken it further.
I've never worked with EEPROM, but with regards to int vs char, you should be able to write a routine to put two chars into an int and then write the int array into EEPROM.
Something like this maybe? You may have to do type coercion to get this to work.

int twoCharsInOneInt = tmpMsg_*256 + tmpMsg[i+1];_
Don't forget to pad the char array if they're not divisible by two.
Good luck!

hari,
thanks for the idea but i think i dont really see what you mean with it.
you mean just cut the whole "message" into 2 int variable's?

Oh I see how you could arrive to that conclusion.
Let's say your message is 7 character long. I propose that you pad it to 8 characters with a space character. Then you take the first two characters and put them into first element of an int array, next you take the third and fourth characters and put them into the second element of the int character, and so on and so forth. When done, you can write the int array to EEPROM. You would reverse the process when your program first starts to see if there's message in EEPROM. I've never worked with EEPROM before, so there maybe an easier way.

EDIT:
I looked at how EEPROM library works. It turned out that it writes bytes not ints.
Here's a test sketch I wrote, you should be able to adapt it.
As you can see it writes one character of msg array at a time to EEPROM, then the second part goes through one character at a time reading it from EEPROM.
Hope this helps.

#include <EEPROM.h>

void setup()
{
  Serial.begin(9600);
  
  //-- Write --
  char msg[] = "Message";
  for (int i = 0; i < (sizeof(msg)-1); i++)
  {
    EEPROM.write(i, msg[i]);
  }
  
  //-- Read --
  char restored[] = "1234567";
  for (int i = 0; i < (sizeof(restored)-1); i++)
  {
    restored[i] = EEPROM.read(i);
  }
  
  Serial.print(restored);
}

void loop()
{
}

Hero Hari!
thanks for your time and mate I LOVE YOU.
you made my day.

at the end of my project i will make pictures and stuff and i will make credit to you.
thanks for it.

Greets,
Spikie

Hi Hari! thank for the code! i was trying to do a clock based on your codes and i had to add numbers to your current library.

ive already added 0-9 under "byte alphabets[][5]"
but how do i modify your codes so they are recognized?

as of now if i try to display the numbers they came out blank on the display. i wished i was able to understand your codes enough to be able to modify them =P

the current code,stock code 0-9 with numbers added

/*
  Arduino 56x8 scrolling LED Matrix
  
Scrolls any message on up to seven (or more?) 8x8 LED matrices.
Adjust the bitmap array below to however many matrices you want to use.
You can start with as few as two.

The circuit:
* 1 8-bit shift register (SN74HC595) to drive the rows of all displays.
* N power 8-bit shift registers (TPIC6C595) to drive the columns (1 chip per display)
* N 8x8 LED matrix display (rows=Anodes, cold=cathodes)
* N * 8 470ohm resistors, one for each column of each display
* 1 10K resistor
* A big breadboard, or several small ones
* Lots and lots of wires. AT LEAST 16 wires for each display.
* If you plan on driving more than 8 displays, you should add 8 transistors to drive the rows because
potentially you would be lighting up the whole row at one time (56 LEDs at once in my case, 8*n in your case)

Wiring tips:
* Key to success is to put the chips on the left and/or right of the matrix rather than above or below.
This would allow you to run wires above and below the matrix without covering any of them.
* I used several power bus breadboard strips above and below the matrix so all row wires never has to cross the matrix.
* Wire up each matrix one at a time, turning on the Ardunio to verify your work before proceeding to the next matrix.
Correcting your work after you have 32 wires over it is very difficult.
* Circuit is available at:
http://g33k.blogspot.com/2010/02/arduino-56x8-scrolling-led-matrix.html

Created Feb 2010
By Hari Wiguna
http://g33k.blogspot.com/
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1267391793

Based on things I learn from the Arduino website.
Special credit to aspro648 creating the alphabet that I use.
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1203747843/22

Creative Commons Attribution-Share Alike 3.0 United States License

*/
// I kept these version notes to show you that it doesn't just all magically happen.  I start simple and keep adding more.
//v01 - Just see if we can drive one matrix, one dot at a time.  Forget multiplexing for now.
//v02 - All three displays show same pattern because they are all tied to same data line.
//v03 - Second and third 595 inputs are tied to outputs of previous, so now shiftout three bytes instead of one.
//v04 - Let's multiplex.  This version has no bleeding, but still flickers quite a bit.
//v05 - Flicker is reduced by using delayMicroseconds() instead of delay().  Flipping both latches HIGH at same time increases brightess of all LEDs.
//v06 - let's animate! (none of this code is in v09, I removed them because they're unrelated to the marquee.
//v07 - We got a scrolling message marquee!
//v08 - Now with SEVEN matrices! 56x8 pixels
//v09 - Add comments and remove experimental code
//v10 - Fixed search and replace error in Plot.  Thanks to Capacea for catching this error.

//-- Columns (Negative Cathodes) --
int latchPin1 = 2; //Arduino pin connected to Green 10 RCK of TPIC6C595
int clockPin1 = 3; //Arduino pin connected to Yellow 15 SRCK of TPIC6C595
int dataPin1 = 4;  //Arduino pin connected to Blue 2 SER IN of TPIC6C595

//-- Rows (Positive Anodes) --
int latchPin2 = 5; //Arduino pinn connected to Green Latch 12 ST_CP / RCK of 74HC595
int clockPin2 = 6; //Arduino pin connected to Yellow Clock 11 SH_CP / SCK of 74HC595
int dataPin2 = 7;  //Arduino pin connected to Blue Data 14 DS / SI of 74HC595

//=== B I T M A P ===
//Bits in this array represents one LED of the matrix
// 8 is # of rows, 7 is # of LED matrix we have
byte bitmap[8][4]; // Change the 7 to however many matrices you want to use.
int numZones = sizeof(bitmap) / 8; // I will refer to each group of 8 columns (represented by one matrix) as a Zone.
int maxZoneIndex = numZones-1;
int numCols = numZones * 8;

//=== F O N T ===
// Font courtesy of aspro648
// http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1203747843/22
// First char is @, next is A, B, etc.  Only lower case, no symbols.  
// The @ will display as space character.
byte alphabets[][5] = {
  {0,0,0,0,0},                  //space
  {31, 36, 68, 36, 31},            //a
  {127, 73, 73, 73, 54},      //b
  {62, 65, 65, 65, 34},            //c
  {127, 65, 65, 34, 28},      //d
  {127, 73, 73, 65, 65},        //e
  {127, 72, 72, 72, 64},      //f
  {62, 65, 65, 69, 38},            //g
  {127, 8, 8, 8, 127},            //h
  {0, 65, 127, 65, 0},            //i
  {2, 1, 1, 1, 126},            //j
  {127, 8, 20, 34, 65},            //k
  {127, 1, 1, 1, 1},            //l
  {127, 32, 16, 32, 127},      //m
  {127, 32, 16, 8, 127},      //n
  {62, 65, 65, 65, 62},            //o
  {127, 72, 72, 72, 48},      //p
  {62, 65, 69, 66, 61},            //q
  {127, 72, 76, 74, 49},      //r
  {50, 73, 73, 73, 38},            //s
  {64, 64, 127, 64, 64},      //t
  {126, 1, 1, 1, 126},            //u
  {124, 2, 1, 2, 124},            //v
  {126, 1, 6, 1, 126},            //q
  {99, 20, 8, 20, 99},            //x
  {96, 16, 15, 16, 96},            //y
  {67, 69, 73, 81, 97},            //z
  {62, 69, 73, 81, 62},         //0 - zero
  {0, 33, 127, 1, 0},           //1
  {49, 67, 69, 73, 49},         //2
  {34, 65, 73, 73, 54},         //3
  {24, 104, 8, 127, 8},         //4
  {114, 73, 73, 73, 70},        //5
  {62, 73, 73, 73, 38},         //6
  {64, 64, 71, 72, 112},        //7
  {54, 73, 73, 73, 54},         //8
  {50, 73, 73, 73, 62},         //9  
};

//=== S E T U P ===

void setup() {
  pinMode(latchPin1, OUTPUT);
  pinMode(clockPin1, OUTPUT);
  pinMode(dataPin1, OUTPUT);

  pinMode(latchPin2, OUTPUT);
  pinMode(clockPin2, OUTPUT);
  pinMode(dataPin2, OUTPUT);
  
  //-- Clear bitmap --
  for (int row = 0; row < 8; row++) {
    for (int zone = 0; zone <= maxZoneIndex; zone++) {
      bitmap[row][zone] = 0;
    }
  }
}

//=== F U N C T I O N S ===

// This routine takes whatever we've setup in the bitmap array and display it on the matrix
void RefreshDisplay()
{
  for (int row = 0; row < 8; row++) {
    int rowbit = 1 << row;
    digitalWrite(latchPin2, LOW);  //Hold latchPin LOW for as long as we're transmitting data
    shiftOut(dataPin2, clockPin2, MSBFIRST, rowbit);   //Transmit data

    //-- Start sending column bytes --
    digitalWrite(latchPin1, LOW);  //Hold latchPin LOW for as long as we're transmitting data

    //-- Shift out to each matrix (zone is 8 columns represented by one matrix)
    for (int zone = maxZoneIndex; zone >= 0; zone--) {
      shiftOut(dataPin1, clockPin1, MSBFIRST, bitmap[row][zone]);
    }

    //-- Done sending Column bytes, flip both latches at once to eliminate flicker
    digitalWrite(latchPin1, HIGH);  //Return the latch pin high to signal chip that it no longer needs to listen for information
    digitalWrite(latchPin2, HIGH);  //Return the latch pin high to signal chip that it no longer needs to listen for information

    //-- Wait a little bit to let humans see what we've pushed out onto the matrix --
    delayMicroseconds(500);
  }
}

// Converts row and colum to actual bitmap bit and turn it off/on
void Plot(int col, int row, bool isOn)
{
  int zone = col / 8;
  int colBitIndex = col % 8;
  byte colBit = 1 << colBitIndex;
  if (isOn)
    bitmap[row][zone] =  bitmap[row][zone] | colBit;
  else
    bitmap[row][zone] =  bitmap[row][zone] & (~colBit);
}

// Plot each character of the message one column at a time, updated the display, shift bitmap left.
void AlphabetSoup()
{
  char msg[] = "LED MATRIX 1234 ";

  for (int charIndex=0; charIndex < (sizeof(msg)-1); charIndex++)
  {
    int alphabetIndex = msg[charIndex] - '@';
    if (alphabetIndex < 0) alphabetIndex=0;
    
    //-- Draw one character of the message --
    // Each character is only 5 columns wide, but I loop two more times to create 2 pixel space betwen characters
    for (int col = 0; col < 7; col++)
    {
      for (int row = 0; row < 8; row++)
      {
        // Set the pixel to what the alphabet say for columns 0 thru 4, but always leave columns 5 and 6 blank.
        bool isOn = 0;
        if (col<5) isOn = bitRead( alphabets[alphabetIndex][col], 7-row ) == 1;
        Plot( numCols-1, row, isOn); // We ALWAYS draw on the rightmost column, the shift loop below will scroll it leftward.
      }
      
      //-- The more times you repeat this loop, the slower we would scroll --
      for (int refreshCount=0; refreshCount < 10; refreshCount++)
        RefreshDisplay();

      //-- Shift the bitmap one column to left --
      for (int row=0; row<8; row++)
      {
        for (int zone=0; zone < numZones; zone++)
        {
          // This right shift would show as a left scroll on display because leftmost column is represented by least significant bit of the byte.
          bitmap[row][zone] = bitmap[row][zone] >> 1;
          
          // Roll over lowest bit from the next zone as highest bit of this zone.
          if (zone < maxZoneIndex) bitWrite(bitmap[row][zone], 7, bitRead(bitmap[row][zone+1],0));
        }
      }
    }
  }
}

//=== L O O P ===

void loop() {
  AlphabetSoup();
}

thanks

Hello.
I see that you've added the bitmap for the digits. Cool.
You've added it to the end of the alphabets array. Although we can make it work there, I think it would be more intuitive if you add them AHEAD of the alphabets. Let me explain...

If you look at an ascii chart

You would see why I subtract '@' in AlphabetSoup().
int alphabetIndex = msg[charIndex] - '@';

The above line would take ASCII code of the nth character of the message to be displayed, subtract the ASCII code for the character '@', and therefore 'A' would be represented by alphabetIndex = 0, 'B' would be '1', etc.

What I would recommend is to include characters '0' through '@' in alphabets array and modify the above line to:
int alphabetIndex = msg[charIndex] - '0'; // That is a zero not the letter 'O'

If you do not want to spend time figuring out the bitmap for ':' through '@', simply use zeroes for those elements so they would appear as blank spaces on the LED matrix. If you're building a clock, you'd want ':' anyway :slight_smile:

Sounds like a fun project.
I'm happy to see everyone taking my code and having fun with it.
Please share video/picture when you got it working. Good luck!

wow thanks for the explanation, that helped a lot

thanks for your kind help! you're the best

hi hari

i encountered some problem and i was thinking if you could help?

i wanted it to display the values obtained from my RTC in decimal numbers. Say like, minute = 10, hour = 5

but from my understanding, void AlphabetSoup() only recognizes a strings of characters. doesn't work if i put values in it =/

any ideas how should i implement it??

Fixed it!

i used the sprintf command and it worked smoothly!

hello all,
last time i added togheter with hari the serial and the eeprom parts.
only the problem is now that i got 5 new matrixes and bigger then the ones i had last time.
since i got those bigger i got the problem is total lost.
the leds are kinda low ( barly give light) and the character are like big blocks. with some leds are more power then others.. can some one maybe give me idea how to fix it?
the only diffrent between the little and big ones are in the big ones they got 2 leds in a dot. but still 64 dots but then with 2 leds each dot.
i was thinking about lowering the resistor but it is not really working.
more weird is that if i just power the matrix by hand. so give pin 4 5v and pin 16 GND it gives normal power and good to see. but when i use with the code and the shift registers it wont work good..
someone maybe have a idea how to fix it?
thanks anyway for the ppl who think with me.
fixxed, dumb me i forget the 5V and GND.
btw the code down here is only working for hari's code.
tested with the hexdecimal version but you need to set all to hexidecimal first.

grz
spikie

p.s.
for the guy above who needed numbers and stuff in his library:

byte alphabets[][5] = {
  {0,0,0,0,0},                       // spatie      ASCII 32
  {0,0,253,0,0},                     // uitroepteken  ASCII 33
  {0,96,0,96,0},                      // "           ASCII 34
  {20,127,20,127,20},                // #           ASCII 35
  {36,42,127,42,18},                    // $           ASCII 36
  {17,2,4,8,17},                        // %           ASCII 37
  {54,73,85,34,5},                      // &           ASCII 38
  {0,0,104,112,0},                      // '           ASCII 39
  {28,34,65},                           // (           ASCII 40
  {65,34,28},                           // )           ASCII 41
  {20,8,62,8,20},                       // *           ASCII 42
  {8,8,62,8,8},                         // +           ASCII 43
  {0,0,5,6,0},                          // ,           ASCII 44
  {8,8,8,8,8},                          // -           ASCII 45
  {0,0,1,0,0},                          // .           ASCII 46
  {1,2,4,8,16},                         // /           ASCII 47
  {62,69,73,81,62},                     // 0           ASCII 48
  {0,33,127,1,0},                       // 1           ASCII 49
  {33,67,69,73,49},                     // 2           ASCII 50
  {66,65,81,105,70},                    // 3           ASCII 51
  {12,20,36,127,4},                     // 4           ASCII 52
  {113,81,81,81,78},                    // 5           ASCII 53
  {30,41,73,73,6},                      // 6           ASCII 54
  {64,64,79,80,96},                     // 7           ASCII 55
  {54,73,73,73,54},                     // 8           ASCII 56
  {48,73,73,74,60},                     // 9           ASCII 57 
  {0,0,54,54,0},                        // :           ASCII 58
  {0,0,53,54,0},                        // ;           ASCII 59
  {0,8,20,34,65},                       // <           ASCII 60
  {20,20,20,20,20},                     // =           ASCII 61
  {0,65,34,20,8},                       // >           ASCII 62
  {32,64,69,72,48},                     // ?           ASCII 63
  {38,73,77,65,62},                     // @           ASCII 64
  {31,36,68,36,31},                     // A           ASCII 65
  {127,73,73,73,54},                    // B           ASCII 66
  {62,65,65,65,34},                     // C           ASCII 67
  {127,65,65,34,28},                    // D           ASCII 68
  {127,73,73,65,65},                    // E           ASCII 69
  {127,72,72,72,64},                    // F           ASCII 70
  {62,65,65,69,38},                     // G           ASCII 71
  {127,8,8,8,127},                      // H           ASCII 72
  {0,65,127,65,0},                      // I           ASCII 73
  {2,1,1,1,126},                        // J           ASCII 74
  {127,8,20,34,65},                     // K           ASCII 75
  {127,1,1,1,1},                        // L           ASCII 76
  {127,32,16,32,127},                   // M           ASCII 77
  {127,32,16,8,127},                    // N           ASCII 78
  {62,65,65,65,62},                     // O           ASCII 79
  {127,72,72,72,48},                    // P           ASCII 80
  {62,65,69,66,61},                     // Q           ASCII 81
  {127,72,76,74,49},                    // R           ASCII 82
  {50,73,73,73,38},                     // S           ASCII 83
  {64,64,127,64,64},                    // T           ASCII 84
  {126,1,1,1,126},                      // U           ASCII 85
  {124,2,1,2,124},                      // V           ASCII 86
  {126,1,6,1,126},                      // W           ASCII 87
  {99,20,8,20,99},                      // X           ASCII 88
  {96,16,15,16,96},                     // Y           ASCII 89
  {67,69,73,81,97},                     // Z           ASCII 90
  {0,127,65,65,0},                      // [           ASCII 91
  {0,0,0,0,0},                          // \           ASCII 92
  {0,65,65,127,0},                      // ]           ASCII 93
  {16,32,64,32,16},                     // ^           ASCII 94
  {1,1,1,1,1},                          // _           ASCII 95
  {0,64,32,16,0},                       // `           ASCII 96
  {2,21,21,21,15},                      // a           ASCII 97
  {127,5,9,9,6},                        // b           ASCII 98
  {14,17,17,17,2},                      // c           ASCII 99
  {6,9,9,5,127},                        // d           ASCII 100
  {14,21,21,21,12},                     // e           ASCII 101
  {8,63,72,64,32},                      // f           ASCII 102
  {24,37,37,37,62},                     // g           ASCII 103
  {127,8,16,16,15},                     // h           ASCII 104
  {0,0,47,0,0},                         // i           ASCII 105
  {2,1,17,94,0},                        // j           ASCII 106
  {127,4,10,17,0},                      // k           ASCII 107
  {0,65,127,1,0},                       // l           ASCII 108
  {31,16,12,16,31},                     // m           ASCII 109
  {31,8,16,16,15},                      // n           ASCII 110
  {14,17,17,17,14},                     // o           ASCII 111
  {31,20,20,20,8},                      // p           ASCII 112
  {8,20,20,12,31},                      // q           ASCII 113
  {31,8,16,16,8},                       // r           ASCII 114
  {2,21,21,21,9},                       // s           ASCII 115
  {16,126,17,1,2},                      // t           ASCII 116
  {30,1,1,2,31},                        // u           ASCII 117
  {28,2,1,2,28},                        // v           ASCII 118
  {30,1,6,1,30},                        // w           ASCII 119
  {17,10,4,10,17},                      // x           ASCII 120
  {24,5,5,5,30},                        // y           ASCII 121
  {17,19,21,25,17},                     // z           ASCII 122
  {0,0,8,54,65},                        // {           ASCII 123
  {0,0,127,0,0},                        // |           ASCII 124
  {65,54,8,0,0},                        // }           ASCII 125

and then futher in the code fix this line:

    int alphabetIndex = restored[charIndex] - '@'; into 
 int alphabetIndex = restored[charIndex] - ' ';

hi Hari!

ive replicate your led matrix using your codes and only 74HC595 on both sides

but for some reason the output i got inverted and not like yours?

is there any reason for that?

hey,
maybe i can help :wink:

if (isOn)
bitmap[row][zone] = bitmap[row][zone] | colBit;
else
bitmap[row][zone] = bitmap[row][zone] & (~colBit);

change the | colBit; with the & (~colBit); in the code and try again?

thanks!

ive added a switch, i can invert the fonts as i wish now =)

hi

I try to use numbers and letters, but does not work. Is there anyone who can help me?

/*
  Arduino 56x8 scrolling LED Matrix
  
Scrolls any message on up to seven (or more?) 8x8 LED matrices.
Adjust the bitmap array below to however many matrices you want to use. 
You can start with as few as two.

The circuit:
* 1 8-bit shift register (SN74HC595) to drive the rows of all displays.
* N power 8-bit shift registers (TPIC6C595) to drive the columns (1 chip per display)
* N 8x8 LED matrix display (rows=Anodes, cold=cathodes)
* N * 8 470ohm resistors, one for each column of each display
* 1 10K resistor
* A big breadboard, or several small ones
* Lots and lots of wires. AT LEAST 16 wires for each display.
* If you plan on driving more than 8 displays, you should add 8 transistors to drive the rows because
potentially you would be lighting up the whole row at one time (56 LEDs at once in my case, 8*n in your case)

Wiring tips:
* Key to success is to put the chips on the left and/or right of the matrix rather than above or below.
This would allow you to run wires above and below the matrix without covering any of them.
* I used several power bus breadboard strips above and below the matrix so all row wires never has to cross the matrix.
* Wire up each matrix one at a time, turning on the Ardunio to verify your work before proceeding to the next matrix.
Correcting your work after you have 32 wires over it is very difficult.
* Circuit is available at: 
http://g33k.blogspot.com/2010/02/arduino-56x8-scrolling-led-matrix.html

Created Feb 2010
By Hari Wiguna
http://g33k.blogspot.com/

Based on things I learn from the Arduino website.
Special credit to aspro648 creating the alphabet that I use.
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1203747843/22

Creative Commons Attribution-Share Alike 3.0 United States License

*/
// I kept these version notes to show you that it doesn't just all magically happen.  I start simple and keep adding more.
//v01 - Just see if we can drive one matrix, one dot at a time.  Forget multiplexing for now.
//v02 - All three displays show same pattern because they are all tied to same data line.
//v03 - Second and third 595 inputs are tied to outputs of previous, so now shiftout three bytes instead of one.
//v04 - Let's multiplex.  This version has no bleeding, but still flickers quite a bit.
//v05 - Flicker is reduced by using delayMicroseconds() instead of delay().  Flipping both latches HIGH at same time increases brightess of all LEDs.
//v06 - let's animate! (none of this code is in v09, I removed them because they're unrelated to the marquee.
//v07 - We got a scrolling message marquee!
//v08 - Now with SEVEN matrices! 56x8 pixels
//v09 - Add comments and remove experimental code
//v10 - Fixed search and replace error in Plot.  Thanks to Capacea for catching this error.

//-- Columns (Negative Cathodes) --
int latchPin1 = 2; //Arduino pin connected to Green 10 RCK of TPIC6C595
int clockPin1 = 3; //Arduino pin connected to Yellow 15 SRCK of TPIC6C595
int dataPin1 = 4;  //Arduino pin connected to Blue 2 SER IN of TPIC6C595

//-- Rows (Positive Anodes) --
int latchPin2 = 5; //Arduino pinn connected to Green Latch 12 ST_CP / RCK of 74HC595
int clockPin2 = 6; //Arduino pin connected to Yellow Clock 11 SH_CP / SCK of 74HC595
int dataPin2 = 7;  //Arduino pin connected to Blue Data 14 DS / SI of 74HC595

//=== B I T M A P ===
//Bits in this array represents one LED of the matrix
// 8 is # of rows, 7 is # of LED matrix we have
byte bitmap[8][4]; // Change the 7 to however many matrices you want to use.
int numZones = sizeof(bitmap) / 8; // I will refer to each group of 8 columns (represented by one matrix) as a Zone.
int maxZoneIndex = numZones-1;
int numCols = numZones * 8;

//=== F O N T ===
// Font courtesy of aspro648
// http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1203747843/22
// First char is @, next is A, B, etc.  Only lower case, no symbols.  
// The @ will display as space character.
byte alphabets[][5] = {
  {0,0,0,0,0},                          // spatie      ASCII 32
  {0,0,253,0,0},                        // uitroepteken  ASCII 33
  {0,96,0,96,0},                        // "           ASCII 34
  {20,127,20,127,20},                   // #           ASCII 35
  {36,42,127,42,18},                    // $           ASCII 36
  {17,2,4,8,17},                        // %           ASCII 37
  {54,73,85,34,5},                      // &           ASCII 38
  {0,0,104,112,0},                      // '           ASCII 39
  {28,34,65},                           // (           ASCII 40
  {65,34,28},                           // )           ASCII 41
  {20,8,62,8,20},                       // *           ASCII 42
  {8,8,62,8,8},                         // +           ASCII 43
  {0,0,5,6,0},                          // ,           ASCII 44
  {8,8,8,8,8},                          // -           ASCII 45
  {0,0,1,0,0},                          // .           ASCII 46
  {1,2,4,8,16},                         // /           ASCII 47
  {62,69,73,81,62},                     // 0           ASCII 48
  {0,33,127,1,0},                       // 1           ASCII 49
  {33,67,69,73,49},                     // 2           ASCII 50
  {66,65,81,105,70},                    // 3           ASCII 51
  {12,20,36,127,4},                     // 4           ASCII 52
  {113,81,81,81,78},                    // 5           ASCII 53
  {30,41,73,73,6},                      // 6           ASCII 54
  {64,64,79,80,96},                     // 7           ASCII 55
  {54,73,73,73,54},                     // 8           ASCII 56
  {48,73,73,74,60},                     // 9           ASCII 57 
  {0,0,54,54,0},                        // :           ASCII 58
  {0,0,53,54,0},                        // ;           ASCII 59
  {0,8,20,34,65},                       // <           ASCII 60
  {20,20,20,20,20},                     // =           ASCII 61
  {0,65,34,20,8},                       // >           ASCII 62
  {32,64,69,72,48},                     // ?           ASCII 63
  {38,73,77,65,62},                     // @           ASCII 64
  {31,36,68,36,31},                     // A           ASCII 65
  {127,73,73,73,54},                    // B           ASCII 66
  {62,65,65,65,34},                     // C           ASCII 67
  {127,65,65,34,28},                    // D           ASCII 68
  {127,73,73,65,65},                    // E           ASCII 69
  {127,72,72,72,64},                    // F           ASCII 70
  {62,65,65,69,38},                     // G           ASCII 71
  {127,8,8,8,127},                      // H           ASCII 72
  {0,65,127,65,0},                      // I           ASCII 73
  {2,1,1,1,126},                        // J           ASCII 74
  {127,8,20,34,65},                     // K           ASCII 75
  {127,1,1,1,1},                        // L           ASCII 76
  {127,32,16,32,127},                   // M           ASCII 77
  {127,32,16,8,127},                    // N           ASCII 78
  {62,65,65,65,62},                     // O           ASCII 79
  {127,72,72,72,48},                    // P           ASCII 80
  {62,65,69,66,61},                     // Q           ASCII 81
  {127,72,76,74,49},                    // R           ASCII 82
  {50,73,73,73,38},                     // S           ASCII 83
  {64,64,127,64,64},                    // T           ASCII 84
  {126,1,1,1,126},                      // U           ASCII 85
  {124,2,1,2,124},                      // V           ASCII 86
  {126,1,6,1,126},                      // W           ASCII 87
  {99,20,8,20,99},                      // X           ASCII 88
  {96,16,15,16,96},                     // Y           ASCII 89
  {67,69,73,81,97},                     // Z           ASCII 90
  {0,127,65,65,0},                      // [           ASCII 91
  {0,0,0,0,0},                          // \           ASCII 92
  {0,65,65,127,0},                      // ]           ASCII 93
  {16,32,64,32,16},                     // ^           ASCII 94
  {1,1,1,1,1},                          // _           ASCII 95
  {0,64,32,16,0},                       // `           ASCII 96
  {2,21,21,21,15},                      // a           ASCII 97
  {127,5,9,9,6},                        // b           ASCII 98
  {14,17,17,17,2},                      // c           ASCII 99
  {6,9,9,5,127},                        // d           ASCII 100
  {14,21,21,21,12},                     // e           ASCII 101
  {8,63,72,64,32},                      // f           ASCII 102
  {24,37,37,37,62},                     // g           ASCII 103
  {127,8,16,16,15},                     // h           ASCII 104
  {0,0,47,0,0},                         // i           ASCII 105
  {2,1,17,94,0},                        // j           ASCII 106
  {127,4,10,17,0},                      // k           ASCII 107
  {0,65,127,1,0},                       // l           ASCII 108
  {31,16,12,16,31},                     // m           ASCII 109
  {31,8,16,16,15},                      // n           ASCII 110
  {14,17,17,17,14},                     // o           ASCII 111
  {31,20,20,20,8},                      // p           ASCII 112
  {8,20,20,12,31},                      // q           ASCII 113
  {31,8,16,16,8},                       // r           ASCII 114
  {2,21,21,21,9},                       // s           ASCII 115
  {16,126,17,1,2},                      // t           ASCII 116
  {30,1,1,2,31},                        // u           ASCII 117
  {28,2,1,2,28},                        // v           ASCII 118
  {30,1,6,1,30},                        // w           ASCII 119
  {17,10,4,10,17},                      // x           ASCII 120
  {24,5,5,5,30},                        // y           ASCII 121
  {17,19,21,25,17},                     // z           ASCII 122
  {0,0,8,54,65},                        // {           ASCII 123
  {0,0,127,0,0},                        // |           ASCII 124
  {65,54,8,0,0},                        // }           ASCII 125
};


//=== S E T U P ===

void setup() {
  pinMode(latchPin1, OUTPUT);
  pinMode(clockPin1, OUTPUT);
  pinMode(dataPin1, OUTPUT);

  pinMode(latchPin2, OUTPUT);
  pinMode(clockPin2, OUTPUT);
  pinMode(dataPin2, OUTPUT);
  
  //-- Clear bitmap --
  for (int row = 0; row < 8; row++) {
    for (int zone = 0; zone <= maxZoneIndex; zone++) {
      bitmap[row][zone] = 255; 
    }
  }
}

//=== F U N C T I O N S ===

// This routine takes whatever we've setup in the bitmap array and display it on the matrix
void RefreshDisplay()
{
  for (int row = 0; row < 8; row++) {
    int rowbit = 1 << row;
    digitalWrite(latchPin2, LOW);  //Hold latchPin LOW for as long as we're transmitting data
    shiftOut(dataPin2, clockPin2, MSBFIRST, rowbit);   //Transmit data

    //-- Start sending column bytes -- 
    digitalWrite(latchPin1, LOW);  //Hold latchPin LOW for as long as we're transmitting data

    //-- Shift out to each matrix (zone is 8 columns represented by one matrix)
    for (int zone = maxZoneIndex; zone >= 0; zone--) {
      shiftOut(dataPin1, clockPin1, MSBFIRST, bitmap[row][zone]);
    }

    //-- Done sending Column bytes, flip both latches at once to eliminate flicker
    digitalWrite(latchPin1, HIGH);  //Return the latch pin high to signal chip that it no longer needs to listen for information
    digitalWrite(latchPin2, HIGH);  //Return the latch pin high to signal chip that it no longer needs to listen for information

    //-- Wait a little bit to let humans see what we've pushed out onto the matrix --
    delayMicroseconds(500);
  }
}

// Converts row and colum to actual bitmap bit and turn it off/on
void Plot(int col, int row, bool isOn)
{
  int zone = col / 8;
  int colBitIndex = col % 8;
  byte colBit = 1 << colBitIndex;
  if (isOn)
    bitmap[row][zone] =  bitmap[row][zone] & (~colBit);
  else
    bitmap[row][zone] =  bitmap[row][zone] | colBit;
}

// Plot each character of the message one column at a time, updated the display, shift bitmap left.
void AlphabetSoup()
{
  char msg[] = "!#$%&-=0123456789ABCDEFGHIJKLMNOPQRSTUVXY ";

  for (int charIndex=0; charIndex < (sizeof(msg)-1); charIndex++)
  {
    int alphabetIndex = msg[charIndex] - '0';
    if (alphabetIndex < 0) alphabetIndex=0;
    
    //-- Draw one character of the message --
    // Each character is only 5 columns wide, but I loop two more times to create 2 pixel space betwen characters
    for (int col = 0; col < 7; col++)
    {
      for (int row = 0; row < 8; row++)
      {
        // Set the pixel to what the alphabet say for columns 0 thru 4, but always leave columns 5 and 6 blank.
        bool isOn = 0; 
        if (col<5) isOn = bitRead( alphabets[alphabetIndex][col], 7-row ) == 1;
        Plot( numCols-1, row, isOn); // We ALWAYS draw on the rightmost column, the shift loop below will scroll it leftward.
      }
      
      //-- The more times you repeat this loop, the slower we would scroll --
      for (int refreshCount=0; refreshCount < 10; refreshCount++)
        RefreshDisplay();

      //-- Shift the bitmap one column to left --
      for (int row=0; row<8; row++)
      {
        for (int zone=0; zone < numZones; zone++)
        {
          // This right shift would show as a left scroll on display because leftmost column is represented by least significant bit of the byte.
          bitmap[row][zone] = bitmap[row][zone] >> 1;
         
          // Roll over lowest bit from the next zone as highest bit of this zone.
          if (zone < maxZoneIndex) bitWrite(bitmap[row][zone], 7, bitRead(bitmap[row][zone+1],0));
        }
      }
    }
  }
}

//=== L O O P ===

void loop() {
  AlphabetSoup();
}