Optimizing code for 16x16

Hello, I've made a 16x16 multiplex for my graduation cap (mostly) following the guide here.

Everything is working and I can make images for its intents and purposes but I was hoping for help with optimizing the code for longer messages. I'll post the code below but it is mostly adapted from the code here.

Since this is a 16x16 matrix, each image that I program takes up ~300 bytes in dynamic memory from being stored in global variables. This means that I can only store about 7 images (though even that is pushing it). I want to learn or at least know if there are better ways of storing these arrays and calling them in the program.

I've already found options such as the one found in this thread but I'm not familiar with that method.

Thanks for any help!

/*
* Show messages on an 16x16 led matrix,
* scrolling from right to left.
*
* Uses FrequencyTimer2 library to
* constantly run an interrupt routine
* at a specified frequency. This
* refreshes the display without the
* main loop having to do anything.
*
*/

#include <FrequencyTimer2.h>

/*#define SPACE{ 
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} 
  }
  */


#define SHIELD { { 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0}, {0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0}, {0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0}, {0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0}, {0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0}, {0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0}, {0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0}, {0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0}, {0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0}, {0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0}, {0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0} }


byte col = 0;
byte leds[16][16];

// pin[xx] on led matrix connected to nn on Arduino (-1 is dummy to make array start at pos 1)
int pins[33]= {-1, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, 15, 16, 17, 18, 19, 20, 21, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42};

// col[xx] of leds = pin yy on led matrix
int cols[16] = {pins[1], pins[2], pins[3], pins[4], pins[5], pins[6], pins[7], pins[8], pins[9], pins[10], pins[11], pins[12], pins[13], pins[14], pins[15], pins[16]};

// row[xx] of leds = pin yy on led matrix
int rows[16] = {pins[32], pins[31], pins[30], pins[29], pins[28], pins[27], pins[26], pins[25], pins[24], pins[23], pins[22], pins[21], pins[20], pins[19], pins[18], pins[17]};


const int numPatterns = 1; 

byte patterns[numPatterns][16][16] = {
  SHIELD 
}; 


int pattern = 0;

void setup() {
  
 // sets the pins as output
 for (int i = 1; i <= 32; i++) {
   pinMode(pins[i], OUTPUT);
 }

 // set up cols and rows
 for (int i = 1; i <= 16; i++) {
   digitalWrite(cols[i - 1], LOW);
 }

 for (int i = 1; i <= 16; i++) {
   digitalWrite(rows[i - 1], LOW);
 }

 clearLeds();

 // Turn off toggling of pin 11
 FrequencyTimer2::disable();
 // Set refresh rate (interrupt timeout period)
 FrequencyTimer2::setPeriod(2000);    
 // Set interrupt routine to be called
 FrequencyTimer2::setOnOverflow(display);

 setPattern(pattern);
}

void loop() {
    pattern = ++pattern % numPatterns;
   slidePattern(pattern, 300);
}

void clearLeds() {
 // Clear display array
 for (int i = 0; i < 16; i++) {
   for (int j = 0; j < 16; j++) {
     leds[i][j] = 0;
   }
 }
}

void setPattern(int pattern) {
   for (int i = 0; i < 16; i++) {
   for (int j = 0; j < 16; j++) {
     leds[i][j] = patterns[pattern][i][j];
   }
 }
}

void slidePattern(int pattern, int del) {
   for (int l = 0; l < 16; l++) {
   for (int i = 0; i < 16; i++) {
     for (int j = 0; j < 16; j++) {
       leds[j][i] = leds[j][i+1];
     }
   }
   for (int j = 0; j < 16; j++) {
     leds[j][15] = patterns[pattern][j][0 + l];
   }
   delay(del);
 }
}

// Interrupt routine
void display() {
 digitalWrite(cols[col], LOW);  // Turn whole previous column off
 col++;
 if (col == 16) {
   col = 0;
 }
 for (int row = 0; row < 16; row++) {
   if (leds[col][15 - row] == 1) {
     digitalWrite(rows[row], LOW);  // Turn on this led
   }
   else {
     digitalWrite(rows[row], HIGH); // Turn off this led
   }
 }
 digitalWrite(cols[col], HIGH); // Turn whole column on at once (for equal lighting times)
}

pack 16 bits in one int, that saves a factor 8 in storage, here in HEX notation

#define SHIELD { 0x3FFC, 0x40FA, ... }

when you use them you can unpack them using masks and some bit math & (and) and >> (shift)

check - Arduino Playground - BitMath -

Hi, try this. I can't test it for you but it compiles OK.

/*
* Show messages on an 16x16 led matrix,
* scrolling from right to left.
*
* Uses FrequencyTimer2 library to
* constantly run an interrupt routine
* at a specified frequency. This
* refreshes the display without the
* main loop having to do anything.
*
*/

#include <FrequencyTimer2.h>

/*#define SPACE{ 
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} 
  }
  */


#define SHIELD { 0b0011111111111100, 0b0100000011111010, 0b0100000011111010, 0b0100000011110110, 0b0100100011111010, 0b0101110011010110, 0b0101010011011010, 0b0100000011101010, 0b0100000010101010, 0b0100000010101010, 0b0100000010101010, 0b0010000010101100, 0b0001000010111000, 0b0000100010110000, 0b0000011011100000, 0b0000000110000000 }


byte col = 0;
byte leds[16][16];

// pin[xx] on led matrix connected to nn on Arduino (-1 is dummy to make array start at pos 1)
char pins[33]= {-1, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, 15, 16, 17, 18, 19, 20, 21, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42};

// col[xx] of leds = pin yy on led matrix
char cols[16] = {pins[1], pins[2], pins[3], pins[4], pins[5], pins[6], pins[7], pins[8], pins[9], pins[10], pins[11], pins[12], pins[13], pins[14], pins[15], pins[16]};

// row[xx] of leds = pin yy on led matrix
char rows[16] = {pins[32], pins[31], pins[30], pins[29], pins[28], pins[27], pins[26], pins[25], pins[24], pins[23], pins[22], pins[21], pins[20], pins[19], pins[18], pins[17]};


const int numPatterns = 1; 

unsigned int patterns[numPatterns][16] = {
  SHIELD 
}; 


int pattern = 0;

void setup() {
  
 // sets the pins as output
 for (int i = 1; i <= 32; i++) {
   pinMode(pins[i], OUTPUT);
 }

 // set up cols and rows
 for (int i = 1; i <= 16; i++) {
   digitalWrite(cols[i - 1], LOW);
 }

 for (int i = 1; i <= 16; i++) {
   digitalWrite(rows[i - 1], LOW);
 }

 clearLeds();

 // Turn off toggling of pin 11
 FrequencyTimer2::disable();
 // Set refresh rate (interrupt timeout period)
 FrequencyTimer2::setPeriod(2000);    
 // Set interrupt routine to be called
 FrequencyTimer2::setOnOverflow(display);

 setPattern(pattern);
}

void loop() {
    pattern = ++pattern % numPatterns;
   slidePattern(pattern, 300);
}

void clearLeds() {
 // Clear display array
 for (int i = 0; i < 16; i++) {
   for (int j = 0; j < 16; j++) {
     leds[i][j] = 0;
   }
 }
}

void setPattern(int pattern) {
   for (int i = 0; i < 16; i++) {
   for (int j = 0; j < 16; j++) {
     leds[i][j] = bitRead(patterns[pattern][i],j);
   }
 }
}

void slidePattern(int pattern, int del) {
   for (int l = 0; l < 16; l++) {
   for (int i = 0; i < 16; i++) {
     for (int j = 0; j < 16; j++) {
       leds[j][i] = leds[j][i+1];
     }
   }
   for (int j = 0; j < 16; j++) {
     leds[j][15] = bitRead(patterns[pattern][j],0 + l);
   }
   delay(del);
 }
}

// Interrupt routine
void display() {
 digitalWrite(cols[col], LOW);  // Turn whole previous column off
 col++;
 if (col == 16) {
   col = 0;
 }
 for (int row = 0; row < 16; row++) {
   if (leds[col][15 - row] == 1) {
     digitalWrite(rows[row], LOW);  // Turn on this led
   }
   else {
     digitalWrite(rows[row], HIGH); // Turn off this led
   }
 }
 digitalWrite(cols[col], HIGH); // Turn whole column on at once (for equal lighting times)
}

Before:

Sketch uses 2,252 bytes (7%) of program storage space. Maximum is 30,720 bytes.
Global variables use 653 bytes (31%) of dynamic memory, leaving 1,395 bytes for local variables. Maximum is 2,048 bytes.

After:

Sketch uses 1,770 bytes (5%) of program storage space. Maximum is 30,720 bytes.
Global variables use 365 bytes (17%) of dynamic memory, leaving 1,683 bytes for local variables. Maximum is 2,048 bytes.

Each image should now take up only 32 bytes instead of 256 bytes.

Paul

Ok I'm probably going to try to work with robtillaart's method because I can get about 3x more space with it than PaulRB's. From my understanding, you're looking at the arrays as if they were a sequence of binary and converting that binary to it's hex value. And when you say unpack them, I unpack the hex value to get back to the binary value (PaulRB's values) which I will read for the output. Does that sound right?

I've never worked with data this way (binary and hex notations) so I'm just trying to make sure I'm getting it.

kejmyers:
Ok I'm probably going to try to work with robtillaart's method because I can get about 3x more space with it than PaulRB's. From my understanding, you're looking at the arrays as if they were a sequence of binary and converting that binary to it's hex value. And when you say unpack them, I unpack the hex value to get back to the binary value (PaulRB's values) which I will read for the output.

No, they are the same. I used binary notation instead of hex to make designing/entering the patterns easier. One compiled, the result is identical.

Hey! I don't suppose you are at the same college as this fellow?

Thanks for the clarification PaulRB, I'm currently studying for my last few finals but I'll test it out as soon as I can.

Paul__B, that's pretty funny, that's not me and I don't think I go to the same school as that person. I already completed my circuit and definitely included resistors in my design. I've been documenting everything I do to make an updated guide about it when it's finished.

Ok so I'm new to bit math but here's what I've come up with so far:

void setPattern(int pattern) {
   for (int i = 0; i < 16; i++) {
   for (int j = 0; j < 16; j++) {
     leds[i][j] = patterns[pattern][i][i & (1<<(15-j))];
   }
 }
}


void slidePattern(int pattern, int del) {
   for (int l = 0; l < 16; l++) {
     for (int i = 0; i < 16; i++) {
       for (int j = 0; j < 16; j++) {
         leds[j][i] = leds[j][j & (1<<(15-i))];
       }
     }
     for (int j = 0; j < 16; j++) {
       leds[j][15] = patterns[pattern][j][j & (1<<(15-l))];
     }
     delay(del);
   }
}

This compiles but doesn't execute properly. I think my understanding of the shift operator is wrong, could anyone help me with this? The original code for the above is below:

void setPattern(int pattern) {
   for (int i = 0; i < 16; i++) {
   for (int j = 0; j < 16; j++) {
     leds[i][j] = patterns[pattern][i][j];
   }
 }
}

void slidePattern(int pattern, int del) {
   for (int l = 0; l < 16; l++) {
   for (int i = 0; i < 16; i++) {
     for (int j = 0; j < 16; j++) {
       leds[j][i] = leds[j][i+1];
     }
   }
   for (int j = 0; j < 16; j++) {
     leds[j][15] = patterns[pattern][j][0 + l];
   }
   delay(del);
 }
}

kejmyers:
could anyone help me with this?

I already did. Did you try my version?

Not possible to say for certain what's wrong with your attempt because you only posted parts of it, but it doesn't look like you have really grasped the concept of what Rob and I are on about. You are still using the patterns[] array as though it has 3 dimensions as it originally did. Our suggestion makes it only 2-D.

I feel really dumb >.< yep yours works, I thought you had just shortened the array, I didn't realize you adjusted the rest of the code as well. I did test the array with my code and it compiled so I didn't even think to look at the rest of the code you wrote. Thank you so much!

Final result for anyone who's curious:

Neat.

That goes on a cap? Pretty big cap!

Yep! One last thing, bitRead() reads from right to left so it was flipping the image horizontally, I fixed the code here:

/*
* Show messages on an 16x16 led matrix,
* scrolling from right to left.
*
* Uses FrequencyTimer2 library to
* constantly run an interrupt routine
* at a specified frequency. This
* refreshes the display without the
* main loop having to do anything.
*
*/

#include <FrequencyTimer2.h>

/*#define SPACE{ 
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 
    {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} 
  }
  */


#define SHIELD { 0b0011111111111100, 0b0100000011111010, 0b0100000011111010, 0b0100000011110110, 0b0100100011111010, 0b0101110011010110, 0b0101010011011010, 0b0100000011101010, 0b0100000010101010, 0b0100000010101010, 0b0100000010101010, 0b0010000010101100, 0b0001000010111000, 0b0000100010110000, 0b0000011011100000, 0b0000000110000000 }


byte col = 0;
byte leds[16][16];

// pin[xx] on led matrix connected to nn on Arduino (-1 is dummy to make array start at pos 1)
char pins[33]= {-1, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 14, 15, 16, 17, 18, 19, 20, 21, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42};

// col[xx] of leds = pin yy on led matrix
char cols[16] = {pins[1], pins[2], pins[3], pins[4], pins[5], pins[6], pins[7], pins[8], pins[9], pins[10], pins[11], pins[12], pins[13], pins[14], pins[15], pins[16]};

// row[xx] of leds = pin yy on led matrix
char rows[16] = {pins[32], pins[31], pins[30], pins[29], pins[28], pins[27], pins[26], pins[25], pins[24], pins[23], pins[22], pins[21], pins[20], pins[19], pins[18], pins[17]};


const int numPatterns = 1; 

unsigned int patterns[numPatterns][16] = {
  SHIELD 
}; 


int pattern = 0;

void setup() {
  
 // sets the pins as output
 for (int i = 1; i <= 32; i++) {
   pinMode(pins[i], OUTPUT);
 }

 // set up cols and rows
 for (int i = 1; i <= 16; i++) {
   digitalWrite(cols[i - 1], LOW);
 }

 for (int i = 1; i <= 16; i++) {
   digitalWrite(rows[i - 1], LOW);
 }

 clearLeds();

 // Turn off toggling of pin 11
 FrequencyTimer2::disable();
 // Set refresh rate (interrupt timeout period)
 FrequencyTimer2::setPeriod(2000);    
 // Set interrupt routine to be called
 FrequencyTimer2::setOnOverflow(display);

 setPattern(pattern);
}

void loop() {
    pattern = ++pattern % numPatterns;
   slidePattern(pattern, 300);
}

void clearLeds() {
 // Clear display array
 for (int i = 0; i < 16; i++) {
   for (int j = 0; j < 16; j++) {
     leds[i][j] = 0;
   }
 }
}

void setPattern(int pattern) {
   for (int i = 0; i < 16; i++) {
   for (int j = 0; j < 16; j++) {
     leds[i][j] = bitRead(patterns[pattern][i],(15-j));
   }
 }
}

void slidePattern(int pattern, int del) {
   for (int l = 0; l < 16; l++) {
   for (int i = 0; i < 16; i++) {
     for (int j = 0; j < 16; j++) {
       leds[j][i] = leds[j][i+1];
     }
   }
   for (int j = 0; j < 16; j++) {
     leds[j][15] = bitRead(patterns[pattern][j],(15-l));
   }
   delay(del);
 }
}

// Interrupt routine
void display() {
 digitalWrite(cols[col], LOW);  // Turn whole previous column off
 col++;
 if (col == 16) {
   col = 0;
 }
 for (int row = 0; row < 16; row++) {
   if (leds[col][15 - row] == 1) {
     digitalWrite(rows[row], LOW);  // Turn on this led
   }
   else {
     digitalWrite(rows[row], HIGH); // Turn off this led
   }
 }
 digitalWrite(cols[col], HIGH); // Turn whole column on at once (for equal lighting times)
}