Arduino Powered 3×8 LED Text Scroller

Where did this project come from?

I bought a chain of LEDs a while ago to make a POV light display in a bicycle wheel. There are many other bike wheel POV projects but they are one color per POV unit. I wanted to make a full color POV display for a bike race I was going to do with my family. We ended up not signing up for the biking event, and as such I never ordered the hall effect sensor for the POV display so I had some extra parts that needed to be purposed. So I decided I would make a 3 by 8 text scroller. It would take strings of characters and scroll very low fidelity versions of them across a 3 by 8 matrix of colored leds and then loop back to the beginning.

Hardware

The hardware setup was fairly simple. I was using an old PC power supply to power the Arduino board and the LEDs. From what I had read the lights could pull 2 amps when at full illumination and the board would pull .5 amps with full PWM both at 5 volts. The max power for the project would be 12.5 watts and with the power supply handling up to 35 I was covered. Unfortunately for this project I never hooked up my Digital Multi Meter to test what the actual usage was. I was running the boards PWM at half rate and the color values I used for the LEDs were also at the lower threshold of their ability because of how bright each one of them was. The one of the nice things about PC power supplies for the an electronics hobbiest is that the older connectors will have a 5 volt wire a 12 volt wire and two grounds. The down side is they have a much higher electrical output potential than a USB cable or battery so you need to be less dumb around them. Combine with that a switch to turn the power supply on and off when needed and they make a good bench power source.

Hooking up the LEDs I used the diagram off of the Adafruit Site that had a very basic wiring diagram and a wrapper library for the lights. I packed the lights into a grid of 3 by 8 plus one on the end. they started at the bottom left and each column started back at the bottom and was added to the right of the one before it. I had made a 5 by 5 grid for a proof of concept I had done before and used hanger wire to hold the lights together. In this case I just used scotch tape for each 3 by 1 column and then taped them all tougher in the end. You may notice a little bowing in the grid and that’s because it was not reinforced.

Putting Together the Software

The programming part of this project was much easier for me. I had continued to have exposure to C++ after college and Wiring, one of the the development platforms for the Arduino, is the nearly the same thing with some minor differences. The challenge with the software was in solving a couple distinct issues. was in in deciding how to represent the characters of the font.

  1. How would each character be represented in data?
  2. How would I store the string for the LEDs?
  3. Based on that how would each character be added to the message?
  4. How would I update the display with new character data as it scrolled?

If you want to skip the explanation check the source out.

Working out a Font

Starting the the characters I would have to come up with a really low fidelity font containing each character I would want to display. My initial idea was to get all the characters to fit into a 3 by 3 grid. This would be easy enough to try. In doing so I ended up finding that using a lower case approximation was a little less ambiguous that most of the upper case alternatives. The fonts ended up looking like the ones below.

A       B X    C XXX   D  X    E XXX   F XXX
  XX      XX     X        XX     XX      XX
  XXX     XX     XXX      XX     XXX     X

Some characters were just hard to work with like E and F and M. One thing about the fonts were that they were 9 bits for a 3 by 3 grid. there are 8 bits in a byte so I would end up having to use 16 bits at a minimum for each character. Thinking of this I realized that the grid for my font was actually 3 by 5. This helped out when trying to render some characters like M.

M
  XXXXX
  X X X

Storing the Grid Data and Writing it to the LEDs

The easiest way to store the data for the scroller would be to use a character string. When scrolling as each character was needed I could add it to the buffer for the LEDs. Though the LEDs are capable of 15 bit color for this project they would be set to just one. Thinking singularly I used bits. The other advantage to this is that to move or scroll the data along the grid I could just use a bit shift. That worked out well considering that the way the LEDs work was to push each character out to the end of the light chain one by one. this meant I could loop though the number of lights in the grid and write each bit to a LED of the grid to an LED.

  for(int i = 0; i < cStripLen; i++){     if((lights >> i) & 1) {
      strip.setPixelColor(i, Color(lightValue,lightValue,lightValue));
    }
    else {
      strip.setPixelColor(i, Color(0,0,0));
    }

Now going from character in a string to the correct character from the font and then adding was a little involved so I’ll cover that after I talk about how the font data was encoded.

The Font as Data

Knowing more about how I would store the grid data encoding the fonts was now doable. If the low bits in the grid would be shifted down to scroll that would mean the new font data would be put in the high end of the grid bits. they would enter the grid at the right and scroll left. This would mean that the left side of the font would have to be put in first. Given those two issues if I wanted to move that font data in 3 bit “columns” I would have to encode that into the font memory first and the later columns would have to be packed in higher in the long that would hold them. With all that I though of some ways of using unions and structs with bit fields to make encoding this easier. However as I backed in the first few characters as test data I found it was much easier to convert the font to binary and encode it as hex that use the unions and structures. Some time I should still try to do that.

Here is a basic example of how the fonts got encoded. Well start by looking at U. To keep the left side of the font in the low order bits we have to start in the lower left of the character and encode it from bottom to top, then move to the next column and repeat. This produced the binary data that would be stored and used as the font. For assignment I converted it to hex;

U      Becomes 000  Column 1: 0 <-- Bit 2 Column 2: 0 <-- Bit 5
  X X          101            1 <-- Bit 1           1 <-- Bit 4
  XXX          111            1 <-- Bit 0           1 <-- Bit 3
Using this pattern the character in binary would be written as:
Bit Position:    4    0
       Value: 1100 1011
       As Hex: CB
//The final character encoding ended up looking like this:
//Setup values for pixel characters
  //Numbers
  pixelChars[0] = 0x1EF;
  pixelChars[1] = 0x7;
  pixelChars[2] = 0x7C;
  pixelChars[3] = 0x1FD;
  //...

  //Characters (+10)
  pixelChars[10] = 0x5B; //a
  pixelChars[11] = 0x1F; //b
  pixelChars[12] = 0x16F; //c
  pixelChars[13] = 0x3B; //d
  pixelChars[14] = 0x17F; //e
  pixelChars[15] = 0x137; //f
  //...

  //Special Chars
  pixelChars[36] = 0x0; //space
  pixelChars[37] = 0x12; //-
  pixelChars[38] = 0x1; //.
  pixelChars[39] = 0x5; //:
  pixelChars[39] = 0x4; //'

Driving the Text on the Grid

So writing the text is fairly simple in concept. So as the grid moves the values for the buffer down they have to be replaced. The next character in the string we want to show should be pulled and written to the buffer. The text in the string we want to display needs to be mapped to the appropriate character in our font. We also have to know where we are in the columns in the font so we can write the correct values to refill the buffer. Finally it would be good to put space in between each character as we print them. Each one of these steps needs to be keep track of while the values in the buffer shift and gets replaced column by column.

void CharacterShifter() {
  //shift lights 3 pixels lower
  lights = lights >> 3;

  //if the pixelCharIndex is at the end (maxPixelCharIndex) then
  //get a new character from the buffer
  if(pixelCharIndex >= maxPixelCharIndex)
  {
    // set the pixelCharIndex to 0 and move to the next char
    pixelCharIndex = 0;
    stringIndex++;

    //then pull the next character from the buffer
    if(textToScroll[stringIndex] == '\0')
    {
      //if the value in the buffer is null then set the position
      //of the buffer to zero
      stringIndex = 0;
    }

    //Since we are changing characters in the buffer insert a one pixel space
    //Which was already done for use when we shifted our bits down 3
  }
  else {
    //print the 3 bits of the value indexed in the buffer
    unsigned int pixelChar = CharToPixelChar(textToScroll[stringIndex]);
    lights = (((unsigned long)(pixelChar >> (3 * pixelCharIndex) & 0x7))
                 << (3 * 7))
                | lights;

    pixelCharIndex++;

    if(pixelCharIndex < maxPixelCharIndex &&         (((pixelChar >> (pixelCharIndex * 3)) && 0x7) == 0))
    {
      pixelCharIndex = maxPixelCharIndex;
    }
  }
  //print the first 3 bits of the value indexed in the buffer
  //increase the pixelCharIndex
  //if maxPixelCharIndex is less than 3 check next three bits
  //if they are equal to zero set the index to maxPixelCharIndex
}

You can see how with each shift how each part of the process is moderated and managed. Now how the font was retrieved was fairly simple as this next snipet shows. With some input checking the array that stores the font can be used as a basic look up table.

unsigned int CharToPixelChar(char c){

  byte index = 0;

  if('0' <= c && c <= '9') index = c - '0';
  else if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')){
    if('a' <= c && c <= 'z') index = c - 'a';
    else index = c - 'A';
    index = index + 10;
  }
  else if ((byte)c == (byte)' ') index = (byte)36;
  else if ((byte)c == (byte)'-') index = (byte)37;
  else if ((byte)c == (byte)'.') index = (byte)38;
  else if ((byte)c == (byte)':') index = (byte)39;

  return pixelChars[index];
}

And finally a video of the result: “This was a triumph – I’m making a note here: HUGE SUCCESS. The LED Scroller is not a lie.”