Sketch examples of scrolling text on 8x8 LED matrix w/ MAX72xx

Thanks for that concise explanation. If I may ask, how did you / and guys/girls on this forum learn about coding to scroll etc. I try to look at different examples and work out how they are doing it but good information on the subject is hard to find. I know that quite a few of you have a solid background in computing but for me its only what I can find online and from help by the generosity of people on this forum. Thanks again for your help

Pedro.

Emdedded programming is hard as it combines both software and hardware, analog and digital.

In this case, you will need to figure out how the hardware is arranged: whether the image is row based or column based, etc. Once you have that set, all you need to do is to figure out how to move the image to get your desired effect and how to code to move that image.

What I found important is to be able to modulize your tasks and figure out a way to easily integrate them back into a workable project. Many of the code pieces out there have all the tasks co-mingled and that makes them useless for your project.

Once you have modulized your code, you can use them in future projects, speeding up the development cycle.

So the more you code, the less you code (incrementally).

Thanks so much for that simple statement "embedded programming" It is the first time during all my internet research into learning to code (specifically Arduino scrolling text) that it has been given a name pointing me to an specific sub genre of programming. All I seem to have read is how easy it all is and it is starting to give me a complex thinking that "I am a bit slow on the uptake" :blush: After googling enbedded prog I am at least finding specific information so thanks again Pedro.

I've made some progress. Example program "LCDemoMatrix" that came with LedControl library had a very clear structure so I can understand it easy (not so much luck with other examples). I realized that characters were stored "sideways" i.e. letter "A" is defined as:

byte a[8]={B00000000,B01111111,B10001000,B10001000,B10001000,B10001000,B01111111,B00000000};

Well I made it bigger and 8x8. So it's very easy to scroll by just moving starting row. So as dhenry mentioned it depends how your text stored (column vs. row) and I think how your LED matrix arranged physically (at which side they touch). So I think in the LedControl they use column-wise text, and other examples on this forum used "row-wise" fonts.
Anyway, I had to do this from scratch so I came up with this way to scroll it on a single matrix:

void text_scroll(int screen){
  
  // Define "A" Character columnwise orientation
  byte a[8]={B00000000,B01111111,B10001000,B10001000,B10001000,B10001000,B01111111,B00000000};
  
  for(int j=pos;j<8;j++){ // This first for loop will shift position of the row
     for(int i=0;i<8;i++) { // This second loop will display character
        lc.setRow(screen,i+j,a[i]); // Display character
    }
    delay(100); // speed of animation
    } 
    pos = -8; // From this point character will advance from "beyond" screen
}

"pos" variable is initially set to 0, so character will start from the middle of the matrix, but then it will always "flow" from the side. Screen specifies which matrix device to address. Don't forget you need to initialize and define them in the beginning (see orignal LCDemoMatrix example).
I'm not quite sure why it works, and how library handles negative rows, but it works :slight_smile:
Of course this is very rough code, and animation doesn't wrap around or continues smoothly to the next screen, but I think I can figure it out. Oh and I didn't use buffer at all yet....
I'll keep this thread updated if anyone find it interesting.

You do have a buffer, in a[].

My approach is to maintain the buffer and always starts at column 0 and finish at column 7 of that buffer. Your approach is is to start with a column, and then wrap around back to it.

My approach involves a lot of data moving around, but is otherwise more flexible - coding a combination move is fairly easy. Your approach involves no data movement, but can be tough to code complex movements.

As to how your code work, think of it having two pieces:

  1. Remember the start column in your image buffer (a[] in this case), in col_start;
  2. for each frame, display columns from col_start to 7; and then display columns from 0 to col_start - 1;
  3. advance col_start.

dhenry:
You do have a buffer, in a[].

Ah I see, I was thinking of it as a font, i.e. something that's constant.

Anyway, I made a lot of progress, now my letter is scrolling seamlessly between 2 LED matrices and even wraps around! :smiley: I think I can adapt it to any number of LED matrices in the chain...

void letter_scroll(){
  // Define "A" Character columnwise orientation
  byte a[8]={B00000000,B01111111,B10001000,B10001000,B10001000,B10001000,B01111111,B00000000};
  int matrix_rows=8; // Specify number of LEDs in a row of the matrix 
  int screen_res=lc.getDeviceCount()*matrix_rows; // Define Screen resolution (lengh) by getting number of devices connected and rows in each matrix (i.e. 2x8=16)
  int row=0; // Var will only be used on first screen to do wrap around function
  for(int j=0;j<screen_res;j++){ // This will shift position of the row creating movement
     for(int i=0;i<8;i++) { // Render Character on Screen 1
       if (j>8){ //we beyond first screen
          if (j>matrix_rows){ // Time to wrap around
            row=j-screen_res; // Modify start position so it goesn't go beyond screen
            lc.setRow(0,i+row,a[i]); // Render Letter
          }
        }
        else{ // We are on the 1st screen
             lc.setRow(0,i+j,a[i]); // Render Letter
         }
     }
     for(int i=0;i<8;i++) { // Render Character on second screen
        if (j>0){ // Are we scrolling off the prev. screen?
          lc.setRow(1,i+j-8,a[i]);  // Render Character on second screen
        }
     }
    delay(100); // speed of animation
    } 
}

LEDScrollTest1.ino (2.32 KB)

bratan:
I'll keep this thread updated if anyone find it interesting.

Hi Bratan,
thanks for sharing your progress, I for one would appreciate you sharing any and all of your forays into "Arduino Scroll World" 8) 8) Now I cannot wait to get home after work and look at your discoveries,

Pedro.

now my letter is scrolling seamlessly

The prior routine suffers from setRow() not wrapping around - if you see its source code, it simply returns when the "row" index is greater than 7 / less than 0. Makes sense for what it does but not for what you want to do.

Your code has two for loops and it can be time consuming. How about this:

  static unsigned char col_starting=0; //starting column
  spiTransfer(addr, 0, a[(col_starting + 0) & 0x07]);
  spiTransfer(addr, 1, a[(col_starting + 1) & 0x07]);
  spiTransfer(addr, 2, a[(col_starting + 2) & 0x07]);
  ...
  spiTransfer(addr, 7, a[(col_starting + 7) & 0x07]);
  col_starting = (col_starting==7)?0:(col_starting+1); //increment index

no loop but 8 transfers. you can modify it for your requirement.

dhenry:
Your code has two for loops and it can be time consuming. How about this:

  static unsigned char col_starting=0; //starting column

spiTransfer(addr, 0, a[(col_starting + 0) & 0x07]);
  spiTransfer(addr, 1, a[(col_starting + 1) & 0x07]);
  spiTransfer(addr, 2, a[(col_starting + 2) & 0x07]);
  ...
  spiTransfer(addr, 7, a[(col_starting + 7) & 0x07]);
  col_starting = (col_starting==7)?0:(col_starting+1); //increment index




no loop but 8 transfers. you can modify it for your requirement.

:astonished: Thanks! Honestly I don't really understand any of it (it's a little too advance for me at this stage), but I will try! :slight_smile: I need to read some more about SPI...

Pedro147:
Hi Brattan,

I just realised that these two code examples do not use Ledcontrol library so when I get home from work I will see what else I have found online and post the link or code for them. But in the meantime -

Scrolltest2 .ino, by Riva on this forum, is attached to the fourth post in this thread Max72XX scrolling text code - #12 by Pedro147 - LEDs and Multiplexing - Arduino Forum

The MAX7219 library I use in Scrolltest2.ino is just a subset of the LedControl library that I hacked to reduce size as original project ran on a ATmega8 and I was running out of space. If you replace the MAX7219 references with LedControl then it should still work okay.
#include <MAX7219.h> becomes
#include <LedControl.h>
and
MAX7219 lc=MAX7219(DIN,CLK,LOAD,numDevices); becomes
LedControl lc=LedControl (DIN,CLK,LOAD,numDevices);

EDIT: I have attached the latest test version of my scrolling code that does kerning that has been modified for the MAX7219 from the Rainbowduino it used to work on. It compiled okay but I don't have a matrix to test it on so beware. Maybe some kind soul will test it and confirm it works okay.

ScrollTest4MAX.ino (14.2 KB)

Riva:
It compiled okay but I don't have a matrix to test it on so beware. Maybe some kind soul will test it and confirm it works okay.[/color]

Thanks Riva, I will give it a try tonight! I soldered 2 matricies with MAX7219 chips and will see it runs on it.
I'm still trying to come up with my own code, but due to lack of programming knowledge, it's hard but fun :slight_smile:

bratan:
Thanks Riva, I will give it a try tonight! I soldered 2 matricies with MAX7219 chips and will see it runs on it.
I'm still trying to come up with my own code, but due to lack of programming knowledge, it's hard but fun :slight_smile:

I have a couple of clocks using 16x8 LED matrix controlled by a pair of MAX7219 chips but one is 400 miles away and the other is not to be messed with #so says the wife :slight_smile: I'm sure you will get the hang of it. I've only been programming C++ since February 2012 though I have used other languages for about 30 years.

30 years! :astonished:
Trying to understand your code, I think I'm starting to get it. Right off the bet, I found tiny issue: need to initialize second display (lc.shutdown(1,false); etc).
Also why bufferLong is 7? Should it be 14 if using 2 displays? Or it doesn't matter?
What does this this code exactly performs?

pgm_read_byte_near(font5x7 + ((ascii - 0x20) * 8) + a);

I understand you are reading it from prog memory and offsetting by 20 characters to get right ASCII, but not sure what "*8" is for and what "font5x7 +" does? Your adding something to an array name not array's element? :~
Sorry for stupid questions :slight_smile:

Edit: Ok I hooked up my LED matrices, and your code totally works... except it looks like I have mine wired/arranged differently from yours :frowning: Instead of scrolling from one matrix to another, it scrolls as if they arranged one on top of each other (i.e. image is rotated 90 degrees clockwise, so it's not going from right to left, but from bottom to the top on each). I need to figure how to to rotate image 90 degrees CW (i.e. rows become columns, etc.).

Riva,

I just tried your ScrollTest 4 MAX code on my Max 7219, 8 x 8 Matrix set up and I can report that it works perfectly. I am not 100% sure, but when I compared it with your ScrollTest 2 version I think that there are two column spaces between chars with the ScrollTest 2 version but only one column space with the ScrollTest 4 version but I may be mistaken as it scrolls quite quickly and I couldn't find the code section that would slow down the scroll speed to verify this. I linked a short video of the 4 version so you can see what you think or if you tell me how to adjust the scroll speed code I will verify my suspicions. Once again excellent nice clean fonts,

Pedro.

p.s. I hope you did not mind me posting this on my youtube channel so I could show you, and if you wish me to remove it just ask and I'll take it off.

bratan:
Also why bufferLong is 7? Should it be 14 if using 2 displays? Or it doesn't matter?

I am using a 5x7 font (5 bits wide x 7 bits deep) but it is stored as 8 bits wide (byte) as this is what processors like and it makes the maths easy XD. The font is only 7 bits tall so I only need 7 buffers but these buffers are longs so are 32 bits wide. What happens when you scroll a character is it's matrix data (8 bits wide) is loaded from font5x7 and places in the lower 8 bits of the 7 bufferLong's (keeping the upper 24 bits) it then reads the 8th byte from the font5x7 matrix data that is the number of bits to scroll this particular character (kerning). It rotates the bufferLong's by 1 bit and then displays the bits 16-23 in the first (left) LED matrix and bits 8-15 in the right LED matrix. It keeps rotating one bit at a time and displaying until the character width has rotated out of the lower 8 bits of the buffer and then goes back to get the next character so scroll.

What does this this code exactly performs?

pgm_read_byte_near(font5x7 + ((ascii - 0x20) * 8) + a);

I understand you are reading it from prog memory and offsetting by 20 characters to get right ASCII, but not sure what "*8" is for and what "font5x7 +" does? Your adding something to an array name not array's element?

The font data is stored as a single dimension byte array (a contiguous block of memory), I need 7 bytes to describe the font data for a single character and the 8th byte is the width of that character in bits (used for kerning). font5x7 point to the starts of this memory so for each increment in the Ascii number I have to jump 8 bytes of this contiguous memory to find the start of that characters font data.

Edit: Ok I hooked up my LED matrices, and your code totally works... except it looks like I have mine wired/arranged differently from yours :frowning: Instead of scrolling from one matrix to another, it scrolls as if they arranged one on top of each other (i.e. image is rotated 90 degrees clockwise, so it's not going from right to left, but from bottom to the top on each). I need to figure how to to rotate image 90 degrees CW (i.e. rows become columns, etc.).

To do this you would need to re-define the font data and alter the way scrolling works, I'm sure someone else has done scrolling this way so if you find the fonts and code it could be adapted to the MAX7219.

Pedro147:
I just tried your ScrollTest 4 MAX code on my Max 7219, 8 x 8 Matrix set up and I can report that it works perfectly. I am not 100% sure, but when I compared it with your ScrollTest 2 version I think that there are two column spaces between chars with the ScrollTest 2 version but only one column space with the ScrollTest 4 version but I may be mistaken as it scrolls quite quickly and I couldn't find the code section that would slow down the scroll speed to verify this. I linked a short video of the 4 version so you can see what you think or if you tell me how to adjust the scroll speed code I will verify my suspicions. Once again excellent nice clean fonts,

Adjust this to alter the scroll speed.

const long scrollDelay = 70;

This version is a slight improvement (in my opinion) on ScrollTest2 as it does character kerning. It only scrolls the width of the character instead of a fixed width of the old version. This makes the display look neater when text has mix of wide and narrow characters like M & I respectfully.
A quick an easy hack to alter the overall spacing between characters is to add the below highlighted line

void loadBufferLong(int ascii){
if (ascii >= 0x20 && ascii <=0x7f){
for (int a=0;a<7;a++){ // Loop 7 times for a 5x7 font
unsigned long c = pgm_read_byte_near(font5x7 + ((ascii - 0x20) * 8) + a); // Index into character table to get row data
unsigned long x = bufferLong [a]; // Load current scroll buffer
x = x | c; // OR the new character onto end of current
bufferLong [a] = x; // Store in buffer
}
byte count = pgm_read_byte_near(font5x7 +((ascii - 0x20) * 8) + 7); // Index into character table for kerning data
count++
for (byte x=0; x<count;x++){
rotateBufferLong();
printBufferLong();
delay(scrollDelay);
}
}
}

Thanks Riva, I don't know how I missed that scroll speed code. Nice touch with the code allowance for varying char widths. It's attention to detail like that that differentiate good from "top shelf" IMHO 8)
Edit - where would I / we be without Google "Kerning is an adjustment of the space between two letters" If I may ask you Riva since you obviously have a long career in programming. How does someone new to all this, learn coding particuarly with Arduino in mind. The general consensus on the forum seems to be to work through all the projects from simple to complex but from how I see it, I feel that there is a better way. Along with progressively trying more complex projects and looking at the code and trying to see how it works, I am doing a little online study in C++. What are your thoughts if I may.

And I'm done! more or less :slight_smile:
I wrote simplified code (as my C++ knowledge is poor), but it works with my setup of 2 matrix displays!

As I was trying to adopt other people's code (Riva yours is brilliant, but didn't work in my hardware arrangement) I realized that I wired my matrices different from most setups. Pins go in one long row (i.e. Pin 12 of matrix 1 is next to Pin 1 of matrix 2), and I actually think this setup easier to wire on breadboards (probably the only way to wire on breadboard) so I'm at loss to understand why mine don't work the same, maybe I wired it different (after all I had to figure out pinouts becuase I had no datasheet, and these are not the same as more popular Sure's displays).
Anyway it doesn't matter, as I figured out code and algorithm of animation. Writing code from scratch is a great educational experience, but it took me almost a week :frowning:
It currently works great with 2 devices, but I'm not sure how much modification will be needed to support 3 or more, I will try to enhance it later to see if I can make it universal for any number of displays.

Here's my algorithm:
Legend: 
B1 bufer 1
B2 bufer 2
NextChar = Font of next character
	1. Fill Buffer B1
	2. Begin Loop . If Looped for 8 times, go to 11
	3. Display B1
	4. Display B2 (it's empty 1st time)
	5. Rotate B1: B1[7]=B1[6], B1[6]=B1[5]...B1[1]=B1[0], B1[0]=NextChar[7]
	6. Rotate B2: B2[7]=B2[6], B2[6]=B2[5]...B2[1]=B2[0], B2[0]=B1[7]
	7. Display B1
	8. Display B2
	9. Delay (animation delay)
	10. Go To 2 (End Loop)
	11. Go to 1

I'm also attaching code (it has font file as well in the rar archive), feel free to use and modify it.
I know font is crappy, I had to do everything from scratch because my characters are rotated 90CCW. Pial's web based 8x8 matrix font generator helped a lot! I will draw a prettier font in the future :slight_smile: I would use other's people nice fonts, if someone can suggest how I can rotate them 90 CCW.
I'm also attaching image that shows how animation works in my code.

Bratan_LED_ScrollTest_version1.rar (3.11 KB)

Got my first youtube video loaded!

Scroll test of 8x32 LED matrix. Can hold up to 233 characters in SRAM. Will be ~40 more once I push 5x8 fonts out to PROGMEM.