Scrolling text routine

Hi all

I have a self-built matrix of 6x6 WS2812B LEDS that I would like to use to scroll a short (literally, four letters) text message across.

I can address the LEDS sequentially, or, thanks to the use of an array, by XY co-ordinates on the grid. I use the Adafruit neopixels library, so the command to address an individual pixel is;

strip.setPixelColor(pixelarray[x][y],Red,Green,Blue);

Where pixelarray is a 6x6 two dimensional array that contains the sequential pixel number at the x,y position.

I have two questions.

The two words I wish to scroll comprise only eight letters in total. Is it best to use separate arrays to hold these "patterns", ie letter A would be;

int LetA[] {8,9,10,11,14,16,19,21,24,25,26,27}

(in my six by six grid that would make a letter "A" that is 5 pixels high and four wide)

Or is there another way to store this data - for instance I was thinking of a 2D array where the first dimension is the letter and the second the co-ordinates, but I don't know if there's actually any advantage to this?

Secondly...how to scroll the info?

I am going to have a go at writing the sketch now...but if anyone has any good ideas, I'd be grateful!

Thanks!

Using an int to store a byte sized value is a waste of memory.

I don't know if there's actually any advantage to this?

You don't? Think about what you have to do to display the letters 'A' and 'T'. Think about what you would have to do to display the 0th and 19th layers in a 3D array.

Then, consider that the layer to display is layer number (letter - 'A').

I'm sure that you'll see that there IS an advantage to having a 3D array.

Secondly...how to scroll the info?

What do you mean by scroll? If you want to display "STOP", and there is room on the display for one full letter at a time, scrolling could mean displaying one full letter at a time, or it could mean showing parts of two letters at a time.

Paul...thanks for the advice...I'm not experienced enough to know what you mean about the memory waste...should I change the assignment of my pixelarray from an int to "byte" values?

As to advantages of an array...yes...as soon as I started lookign at coding it I figured out there IS an advantage.

As what I wanted was a "smooth" scroll - ie, the whole word, not letter by letter, for me the simplest (not necessarily best) way was to put the whole word in a 2D array. Then I realised that my 6x6 pixel array was really just a moving "window" along the array that contains the word.

In short...this works...it may not be memory efficient, pretty or clever...but it works!

//Scrolling letters

#include <Adafruit_NeoPixel.h>

#define N_LEDS           36   //LEDS in strip
#define PIN               8   //output pin 1
Adafruit_NeoPixel strip = Adafruit_NeoPixel(N_LEDS, PIN, NEO_GRB + NEO_KHZ800);

void setup() {

  int p;
  Serial.begin(9600);
  randomSeed(analogRead(0));
  strip.setBrightness(50);
  strip.begin();

  // Test pixels

  strip.setPixelColor (0, 0, 50, 0);
  strip.setPixelColor (5, 0, 50, 0);
  strip.setPixelColor (30, 0, 50, 0);
  strip.setPixelColor (35, 0, 50, 0);
  strip.show();
  delay(200);
  // clear all pixels
  for (p = 0; p <= (N_LEDS - 1); p++) {
    strip.setPixelColor(p, 0, 0, 0);
  }
  strip.show();

}
void loop() {

  //(0,0) bottom left, (max,max) top right
  int pixelarray[6][6] = {
    {   5,  6,  17,  18,  29,  30,   },
    {   4,  7,  16,  19,  28,  31,   },
    {   3,  8,  15,  20,  27,  32,   },
    {   2,  9,  14,  21,  26,  33,   },
    {   1, 10,  13,  22,  25,  34,   },
    {   0, 11,  12,  23,  24,  35,   },
  };
  int arraysize = 0;
  int lightpitch = 1;
  int RVa = 255;
  int GVa = 255;
  int BVa = 255;
  int p; //pixel number
  int x;
  int y;
  int i;
  int n;
  int KING[6][20] = {
    {1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0,},
    {1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1,},
    {1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0,},
    {1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1,},
    {1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1,},
    {1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0,},
  };
  int HERO[6][19] = {
    {1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0,},
    {1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1,},
    {1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1,},
    {1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1,},
    {1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1,},
    {1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0,},
  };

  //scroll "King"
  arraysize = sizeof(KING[0]) / sizeof(int);
  Serial.print("KING contains ");
  Serial.print(arraysize);
  Serial.println(" elements");
  for (i = 0; i <= arraysize - 6; i++) {
    for (x = 0; x <= 5; x++) {
      for (y = 5; y >= 0; y--) {
        strip.setPixelColor(pixelarray[x][y], RVa * KING[5 - y][i + x], 0, 0);
      }
    }
    strip.show();
    delay(500);
  }
}

should I change the assignment of my pixelarray from an int to "byte" values?

Yes, unless you plan to have more than 255 rows or columns in your matrix.

it may not be memory efficient, pretty or clever...but it works!

It is going to be challenging to extend that to other words. Being able to display an arbitrary phrase may not be important to you, but others might like to borrow your code and schematics, and would want to display different words.

I take your point Paul...though to be honest it took me all of a few minutes to work out the array for the words I needed.

My way is simple because I decided I only wanted to scroll in one direction, so basically I could just "read" my word, column by column from the array. The three nested loops i, x and y act as instructions to;

read the values from “column” i in the word array
write the values to column "x" on the LED display
for each of y rows on the display

Since last post I've added a "lead in" scroll and a "wipe out" scroll so that the first letter slides into view and the last letter slides out, instead of them just suddenly appearing and disappearing.

I suppose if I wanted more flexibility I'd have to build an array that contained a 6x6 matrix for each letter of the alphabet, plus any numbers or symbols, plus upper and lower case etc etc...

Not that I'm going to right now but as you say, for the benefit of others...assuming just a-z would that actually mean an array of [26][6][6]? I think I'd get quite lost laying that out...x columns and y rows I can keep quite nicely visualised...adding in the third dimension makes my head hurt...

Sounds like that could get pretty big so I understand your concerns over memory. Thank you for your input - I'm just pleased that I got it working at all, as when I posted I really had NO idea where to start!

Not that I'm going to right now but as you say, for the benefit of others...assuming just a-z would that actually mean an array of [26][6][6]? I think I'd get quite lost laying that out...x columns and y rows I can keep quite nicely visualised...adding in the third dimension makes my head hurt...

The columns and rows form layers. If you have a layer for each letter, it's just like drawing on 26 (36 of you want numbers, too) sheets of paper, and stacking them up.

You don't need 6 bytes per column or row to hold 6 bits (an LED is on or off, it only takes one bit to define that). You could pack all the data for one letter in 6 bytes. So, 36 letters and digits would only take 216 bytes.

Since your "characters" are only 4 pixels wide, you really could cut that down to 3 bytes per letter, but the packing and unpacking is messier. Using one byte per column means 4 bytes per letter, so 36 letters and numbers would really only need 144 bytes.

Compare that to the 240 bytes you are using now to display/scroll "KING".

Regardless, you have done a great job of finding a way to accomplish your goal. There is room for improvement, but if you are happy, stop now. If you want to challenge yourself, learn about bitRead() and see if you can figure out how to replace the 6 ints for one column in KING with one byte.

Then, define K as 4 bytes.

Then, figure out to combine the bytes in K, I, N, and G to form the (replacement for the current) array called KING.

Once you can do that, and have defined all 26 (or 36) letters (and numbers), forming an arbitrary phrase will be very simple, if you limit the phrase length to say 20 characters. When you minimize the amount of data for one letter, from 36 to 48 bytes per letter to 4, you'll see that you have a lot more memory available, and will be able to support longer phrases. Of course scrolling the Declaration of Independence on your 6 pixel wide display will probably never really work, but, hey, you'll have learned a lot.

PaulS:
You don't need 6 bytes per column or row to hold 6 bits (an LED is on or off, it only takes one bit to define that). You could pack all the data for one letter in 6 bytes. So, 36 letters and digits would only take 216 bytes.

Lost me a bit here...yes an led is on or off...but there are 6 on a row and six on a column.

Even though my letters are four pixels wide, that means each letter is 24 bytes, surely?

Unless you’re talking about only recording the LEDs which are on (the rest by default, being off). I did consider that, but it meant the arrays would be different sizes and I couldn’t think how to identify spacing...so yes the array for “H” could just be a series of 14 “ones”, but how do I....

...oh...

...lightbulb...

Six columns of data. Zeros or ones.

Binary numbers up to 32? Am I on the right track?

Even though my letters are four pixels wide, that means each letter is 24 bytes, surely?

No. There are 24 0s or 1s to define a letter. That is 24 bits, not bytes. You can store 8 bits in a byte, although it might be easier to 1 byte per row or column, so 4 or 6 bytes, not 24.

Bah. I should have known that. I’ve seen Tron. Back to school for me...