Running out of SRAM, PROGMEM question.

My program is currently running out of SRAM, what I have is a program that runs a scrolling LED sign. What I'm wondering is if I can use a PROGMEM function to store some of the items into flash memory. Reading through the PROGMEM page on the arduino website I'm guessing no but I could be wrong. The problem I see is that a lot of the data is stored in a custom variable type. My code is below:

int Row1 = 2;
int Row2 = 3;
int Row3 = 4;
int Row4 = 5;
int Row5 = 6;
int Col6 = 7;
int Col5 = 8;
int Col4 = 9;
int Col3 = 10;
int Col2 = 11;
int Col1 = 12;
int Tx = 0;
int Ty = 0;
int y = 0;
int z = 0;
int WordSize = 0;
unsigned long t = 0;
//this is what defines a Letter on the most primitive letter
struct BaseLetter {
      virtual char get( byte line, byte bit ) = 0;
      virtual byte width( void ) = 0;

      static const byte NUMBER_OF_LINES = 5;
};

//Define letters of variable width using this template struct
template<byte size>
struct Letter : public BaseLetter {
      Letter( byte l0, byte l1, byte l2, byte l3, byte l4) :
            line0(l0),line1(l1),line2(l2),line3(l3),line4(l4) {
      }
      unsigned line0 : size;
      unsigned line1 : size;
      unsigned line2 : size;
      unsigned line3 : size;
      unsigned line4 : size;
      unsigned : 0;

      virtual char get( byte line, byte bit ) {
            switch (line){
                  case 0: return (line0&(1<<bit%size)?1:0); break;
                  case 1: return (line1&(1<<bit%size)?1:0); break;
                  case 2: return (line2&(1<<bit%size)?1:0); break;
                  case 3: return (line3&(1<<bit%size)?1:0); break;
                  case 4: return (line4&(1<<bit%size)?1:0); break;
            }
            return -1;
      }
      virtual byte width() { return size; }
};

/*
DEFINE THE LETTERS
*/
Letter<3> A = Letter<3>(
      B010,
      B101,
      B111,
      B101,
      B101
);
Letter<3> B = Letter<3>(
      B110,
      B101,
      B110,
      B101,
      B110
);
Letter<3> C = Letter<3>(
      B011,
      B100,
      B100,
      B100,
      B011
);
Letter<3> D = Letter<3>(
      B110,
      B101,
      B101,
      B101,
      B110
);
Letter<3> E = Letter<3>(
      B111,
      B100,
      B111,
      B100,
      B111
);
Letter<3> F = Letter<3>(
      B111,
      B100,
      B110,
      B100,
      B100
);
Letter<5> G = Letter<5>(
      B01110,
      B10000,
      B10011,
      B10001,
      B01110
);
Letter<3> H = Letter<3>(
      B101,
      B101,
      B111,
      B101,
      B101
);
Letter<3> I = Letter<3>(
      B111,
      B010,
      B010,
      B010,
      B111
);
Letter<3> J = Letter<3>(
      B011,
      B001,
      B001,
      B101,
      B010
);
Letter<4> K = Letter<4>(
      B1001,
      B1010,
      B1100,
      B1010,
      B1001
);
Letter<3> L = Letter<3>(
      B100,
      B100,
      B100,
      B100,
      B111
);
Letter<5> M = Letter<5>(
      B10001,
      B11011,
      B10101,
      B10001,
      B10001
);
Letter<5> N = Letter<5>(
      B10001,
      B11001,
      B10101,
      B10011,
      B10001
);
Letter<3> O = Letter<3>(
      B010,
      B101,
      B101,
      B101,
      B010
);
Letter<3> P = Letter<3>(
      B110,
      B101,
      B110,
      B100,
      B100
);
Letter<5> Q = Letter<5>(
      B01110,
      B10001,
      B10101,
      B10011,
      B01100
);
Letter<4> R = Letter<4>(
      B1110,
      B1010,
      B1100,
      B1010,
      B1001
);
Letter<3> S = Letter<3>(
      B011,
      B100,
      B010,
      B001,
      B110
);
Letter<3> T = Letter<3>(
      B111,
      B010,
      B010,
      B010,
      B010
);
Letter<3> U = Letter<3>(
      B101,
      B101,
      B101,
      B101,
      B010
);
Letter<5> V = Letter<5>(
      B10001,
      B10001,
      B10001,
      B01010,
      B00100
);
Letter<5> W = Letter<5>(
      B10001,
      B10001,
      B10001,
      B10101,
      B01010
);
Letter<3> X = Letter<3>(
      B101,
      B101,
      B010,
      B101,
      B101
);
Letter<3> Y = Letter<3>(
      B101,
      B101,
      B010,
      B010,
      B010
);
Letter<3> Z = Letter<3>(
      B111,
      B001,
      B010,
      B100,
      B111
);
Letter<1> Ls = Letter<1>(
      B0,
      B0,
      B0,
      B0,
      B0
);
Letter<2> Ws = Letter<2>(
      B00,
      B00,
      B00,
      B00,
      B00
);

/*
DEFINE THE WORD
*/
const byte WORD_LENGTH = 21;//MAX is around 26
//BaseLetter* word1[WORD_LENGTH] = {&H, &Ls, &E, &Ls, &L, &Ls, &L, &Ls, &O, &Ws, &W, &Ls, &O, &Ls, &R, &Ls, &L, &Ls, &D}; //Hello world 19
BaseLetter* word1[WORD_LENGTH] = {&N, &Ls, &A, &Ls, &T, &Ls, &E, &Ls, &S, &Ws, &A, &Ws, &B, &Ls, &I, &Ls, &T, &Ls, &C, &Ls, &H}; // nates a bitch 21
//BaseLetter* word1[WORD_LENGTH] = {&H, &Ls, &E, &Ls, &L, &Ls, &L, &Ls, &O, &Ws, &W, &Ls, &O, &Ls, &R, &Ls, &L, &Ls, &D}; //hello world
//BaseLetter* word1[WORD_LENGTH] = {&T, &Ls, &H, &Ls, &X, &Ws, &A, &Ls, &L, &Ls, &P, &Ls, &H, &Ls, &A, &Ls, &B, &Ls, &E, &Ls, &T, &Ls, &A}; //thx Alphabeta 23
char Text[5][5*(WORD_LENGTH/2)+(WORD_LENGTH/2)];


void setup() {
  pinMode(Row1, OUTPUT);//Row1
  pinMode(Row2, OUTPUT);//Row2
  pinMode(Row3, OUTPUT);//Row3
  pinMode(Row4, OUTPUT);//Row4
  pinMode(Row5, OUTPUT);//Row5
  pinMode(Col6, OUTPUT);//Col6
  pinMode(Col5, OUTPUT);//Col5
  pinMode(Col4, OUTPUT);//Col4
  pinMode(Col3, OUTPUT);//Col3
  pinMode(Col2, OUTPUT);//Col2
  pinMode(Col1, OUTPUT);//Col1
  Serial.begin(9600);
  
  for(int i=0; i<WORD_LENGTH; i++){
    WordSize = WordSize + (word1[i]->width());
  }

  for(Ty=0;Ty<(5*(WORD_LENGTH/2)+(WORD_LENGTH/2));Ty++){
     for(Tx=0;Tx<=5;Tx++){
       Text[Tx][Ty]=0;
     }
   }
   Tx=0;
   Ty=0;
   for (int wordIndex=0; wordIndex<WORD_LENGTH; wordIndex++){
            //loop through all the columns
        for (int bit=(word1[wordIndex]->width()-1); bit>=0; bit--){
                  //loop through all the rows
            for (int line=0; line<BaseLetter::NUMBER_OF_LINES; line++){
                     //print the value at the current letter, at the current line at the current bit
                     //Serial.print(word1[wordIndex]->get(line,bit),DEC);
                           Text[Tx][Ty]=int(word1[wordIndex]->get(line,bit));
                           Tx++;
                           //Serial.print(Text[Tx][Ty],DEC);
                  }
                  //Serial.println();
                        Tx=0;
                        Ty++;
            }
      }
        Tx = 0;
        Ty = 0;
        for(Tx=0;Tx<5;Tx++){
          for(Ty=0;Ty<(5*(WORD_LENGTH/2)+(WORD_LENGTH/2));Ty++){
            Serial.print(Text[Tx][Ty],DEC);
          }
          Serial.println();
        }
}

void loop() {
   Tx = 0;
   Ty = 0;

  for(y=0; y <6; y++){
    if(t<100){
      t++;}
    else{
      z++;
      t=0;}
    if(z==((5*(WORD_LENGTH/2)+(WORD_LENGTH/2))-6))
    {z=0;}
    digitalWrite(Row1, Text[0][y+z]);
    digitalWrite(Row2, Text[1][y+z]);
    digitalWrite(Row3, Text[2][y+z]);
    digitalWrite(Row4, Text[3][y+z]);
    digitalWrite(Row5, Text[4][y+z]);
    //delay(1000);
    if(y==0){
      pinMode(Col1,OUTPUT);
      digitalWrite(Col1, LOW);
      pinMode(Col2,INPUT);
      pinMode(Col3,INPUT);
      pinMode(Col4,INPUT);
      pinMode(Col5,INPUT);
      pinMode(Col6,INPUT);}

    if(y==1){
      //Serial.println("Col2");
      pinMode(Col2,OUTPUT);
      pinMode(Col1,INPUT);
      digitalWrite(Col2, LOW);
      pinMode(Col3,INPUT);
      pinMode(Col4,INPUT);
      pinMode(Col5,INPUT);
      pinMode(Col6,INPUT);}
    if(y==2){
      pinMode(Col3,OUTPUT);
      pinMode(Col1,INPUT);
      pinMode(Col2,INPUT);
      digitalWrite(Col3, LOW);
      pinMode(Col4,INPUT);
      pinMode(Col5,INPUT);
      pinMode(Col6,INPUT);}
    if(y==3){
      pinMode(Col4,OUTPUT);
      pinMode(Col1,INPUT);
      pinMode(Col2,INPUT);
      pinMode(Col3,INPUT);
      digitalWrite(Col4, LOW);
      pinMode(Col5,INPUT);
      pinMode(Col6,INPUT);}
    if(y==4){
      pinMode(Col5,OUTPUT);
      pinMode(Col1,INPUT);
      pinMode(Col2,INPUT);
      pinMode(Col3,INPUT);
      pinMode(Col4,INPUT);
      digitalWrite(Col5, LOW);
      pinMode(Col6,INPUT);}
    if(y==5){
      pinMode(Col6,OUTPUT);
      pinMode(Col1,INPUT);
      pinMode(Col2,INPUT);
      pinMode(Col3,INPUT);
      pinMode(Col4,INPUT);
      pinMode(Col5,INPUT);
      digitalWrite(Col6, LOW);}
    delay(1);
  }
  y=0;

}

As you can see I have the Custom Letter<> variable and I believe that is what's taking up a lot of the space. If I could off load that to flash it would mean i could make longer sentences in my program. I should mention this is a program that generates an array of 1s and 0s which is then feed to a scrolling LED sign.

Hi,

  1. use #define for the row and col definitions.
  2. try to move the letter definitions to flash because they are constant.

Mike

I'm not sure how to make the letter definitions constant. If I place "const" in front of Letter<3> and then again in front of BaseLetter* I get an error when I try and use the width() function of the Letters. I think the problem is that each letter can be any width and they are not all a constant width. If I change the Letter code from virtual byte width to const byte width I get the error "initializer specified for non-virtual method const byte width..."

I have seen it claimed that declaring a variable CONST does not actually save any RAM:
http://www.nongnu.org/avr-libc/user-manual/pgmspace.html

Using the Flash library makes it easier to use PROGMEM:

http://arduiniana.org/libraries/flash/

There is no reason that complex data structures can't be put into PROGMEM nearly as easily, although in fetching the data you'll have to be a bit clumsy and use pgm_read_byte() and similar instead of just structure references. For example, I have some code that defines a "menu" datastructure that can be in PROGMEM on an AVR but regular memory under linux (or whatever) for debugging (or whatever.) It looks like:

typedef const void (*menu_action_t)(void);

typedef const struct menu_item_ {
    unsigned char menu_key;
    menu_action_t menu_action;      /* function or menu pointer */
} menu_item_t;

const menu_item_t mymenu[]  PROGMEM = { /* blah blah */ };

#if defined MENUS_IN_PROGMEM
#define MENU_GETKEY(menu) (pgm_read_byte(&menu->menu_key))
#define MENU_GETACTION(menu) (pgm_read_word(&menu->menu_action))
#else
#define MENU_GETKEY(menu) (menu->menu_key)
#define MENU_GETACTION(enu) (menu->menu_action)
#endif

Other optimizations are: decrease the size of the serial buffer.

But much easier: use a bigger chip. That is go from 168 to 324. If this does not suffice go to 644 "Sangduino" . Still not enough, go to the Arduino mega with 8k. Still not enough? Do not use an Arduino anymore :wink:

But for the scrolling LED sign I would expect that 1k Ram is more than enough once you switch to PROGMEM.

Cheers, Udo