Global variables exceeds the limit

Hi everyone,

I made an LED flasher with multiple patterns, and everything was working fine until I decided to use the IRRemote library to control the lights with an IR remote: since then I can't upload my sketch because the "data section exceeds available space in board" -> global variable uses 3294 bytes (160%), limit being 2048.

-> without the IRRemote.h (and the code related to IR in the loop) my code works fine.

I don't use String at all in my code, just millis and arrays to control the LED state (on / off).
I'm using an arduino pro mini 5V / 16MHz (because of size constrains I can't use anything else).

Here is my code:

#include <IRremote.h>        
IRrecv irrecv(A4);     
decode_results results;  

byte level;
bool traffic_advisor_ON = true;
bool primary_light_ON = true;
bool BLINKER = false;
bool headlight = false;
int TA_pattern = 0;
int WHELEN_pattern = 1;
int BLINKER_pattern = 1;

Thanks for the help

Leave your big constant arrays in PROGMEM.

thanks for the fast reply, I did some research and in addition to add PROGMEM after const as in "const seq_entry PROGMEM WHELEN1_SEQUENCE[] =" I would also have to edit the part where I call the array value correct ? Meaning I'd have to change this code and add "pgm_read_byte" when calling the array correct:

for (int a = 0; a < PINS_NO; a++)
		  {
			if ((WHELEN1_SEQUENCE[WHELEN_SEQUENCE_INDEX].pattern & (0x1 << a)) != 0)
			{
			  level = HIGH;
			}
			else
			{
			  level = LOW;
			}

			digitalWrite(PINS[a], level);
		  }

		  intervalLength = WHELEN1_SEQUENCE[WHELEN_SEQUENCE_INDEX].interval;

		  WHELEN_SEQUENCE_INDEX++;

		  if (WHELEN_SEQUENCE_INDEX == WHELEN1_SEQUENCE_NO)
		  {
			WHELEN_SEQUENCE_INDEX = 0;
		  }

I'd love to get some guidelines here if possible as I'm a newbie at programming. Thanks !
Also by any chance, how could I possibly not use PROGMEM and optimize memory ? (most posts I found on internet mention things about strings but since I'm not using any strings ...)

One step is to stop using "int", two bytes, anywhere a single byte can hold the value you want.
Paul

You can't avoid using PROGMEM; the trick is avoiding copying things in PROGMEM to precious RAM when you don't need to.

I tried to find ways to help you avoid PROGMEM but there's no way. I imagine you eventually would need to use it even if I gave you a trick to make it work now. So you have a few sequences and you're running out of SRAM. What happens if you went with PROGMEM and then ran out of that after a few dozen sequences because you became more interested in expanding your work?

Would adding an sd card be possible for you? One thing is the storage amount would be "infinite" for sequences. Another thing is you can save these sequences to files as binary values and load each one into a memory buffer before playing it, making your code more generic. Just provide a file name and load into memory and play.

It would be a great way to learn another important piece of hardware, sd card. Interested?

Unfortunately SD card is not an option, I work on a very restricted space and it's already hard enough to fit everything.
I do not plan on adding more sequence / animation, the code I made won't change in the future (or i'll just edit the current sequence I made).
I might have found a temporary trick: I don't use decode_results results, I directly use "switch(decode_results())": not storing the results in a global variable seems to have dropped the memory usage. I'd have to test tomorrow to see if receiving the IR signal works though.

Can you really justify those datatypes?

I did this test. It seems to work. You can try this yourself at cpp.sh.

#include <stdio.h>
#include <inttypes.h>
struct test
{
    uint16_t pattern:10;
    uint16_t interval:6;
};
int main()
{
    struct test a={0x55,10};
    printf("%x:%d",a.pattern,a.interval*5);
}

Do make an effort to understand what @TheMemberFormerlyKnownAsAWOL meant. My fix needs that understanding. It assumes all of your intervals are multiples of 5 and are between 0 and 315 so the interval in the struct is the actual interval divided by 5, to squeeze the values together to take less memory.

Since your intervals are all under 65000 one easy change is to make .interval 'uint16_t' instead of 'uint32_t'. That's not enough to fit in an UNO/Nano/Mini so you really need PROGMEM. A quick way was to replace all "[]" with "[] PROGMEM ".

Then you need to fetch the data from PROGMEM. This is going to involve a lot of changes because the code to go through a sequence was duplicated for each sequence.
x_SEQUENCE[x_SEQUENCE_INDEX].pattern
becomes:
pgm_read_word(&x_SEQUENCE[x_SEQUENCE_INDEX].pattern)
and
intervalLength = x_SEQUENCE[x_SEQUENCE_INDEX].interval;
becomes
intervalLength = pgm_read_word(&x_SEQUENCE[x_SEQUENCE_INDEX].interval);

You should move the code to play a sequence into a function. Then your 'switch/case' cases can be reduced to a single line each. By adding arguments for the list of pins and the number of pins you can use the same 'playSequence()' for all sequences. That would mean the 'pgm_read_word()' calls would be limited to that one function. Much easier to maintain.

void playSequence(const uint16_t *pinArray, size_t pinCount, const seq_entry *sequence, size_t count, uint16_t &index)
{
  if (index >= count)
  {
    index = 0;
  }

  for (size_t a = 0; a < pinCount; a++)
  {
    uint16_t pat = pgm_read_word(&sequence[index].pattern);

    digitalWrite(pinArray[a], ((pat & (0x1 << a)) == 0) ? LOW : HIGH);
  }

  intervalLength = pgm_read_word(&sequence[index].interval);

  index++;

  if (index >= count)
  {
    index = 0;
  }
}

/******************************************************************************  ANIMATIONS ***********************************************************************/
void WHELEN_ANIMATION()
{
  switch (WHELEN_pattern)
  {
    case 1:  playSequence(PINS, PINS_NO, WHELEN1_SEQUENCE, WHELEN1_SEQUENCE_NO, WHELEN_SEQUENCE_INDEX); break;
    case 2:  playSequence(PINS, PINS_NO, WHELEN2_SEQUENCE, WHELEN2_SEQUENCE_NO, WHELEN_SEQUENCE_INDEX); break;
    case 3:  playSequence(PINS, PINS_NO, WHELEN3_SEQUENCE, WHELEN3_SEQUENCE_NO, WHELEN_SEQUENCE_INDEX); break;
  }
}

/*
  void GRILL_ANIMATION()
  {
  playSequence(GRILL_PINS, GRILL_PINS_NO, GRILL_SEQUENCE, GRILL_SEQUENCE_NO, GRILL_SEQUENCE_INDEX); break;
  }

*/

void BLINKER_ANIMATION()
{
  switch (BLINKER_pattern)
  {
    case 1:  playSequence(BLINKER_PINS, BLINKER_PINS_NO, BLINKER_L_SEQUENCE, BLINKER_L_SEQUENCE_NO, BLINKER_SEQUENCE_INDEX); break;
    case 2:  playSequence(BLINKER_PINS, BLINKER_PINS_NO, BLINKER_R_SEQUENCE, BLINKER_R_SEQUENCE_NO, BLINKER_SEQUENCE_INDEX); break;
    case 3:  playSequence(BLINKER_PINS, BLINKER_PINS_NO, BLINKER_4_SEQUENCE, BLINKER_4_SEQUENCE_NO, BLINKER_SEQUENCE_INDEX); break;
  }
}



void TA_ANIMATION()
{
  switch (TA_pattern)
  {
    case 0:  playSequence(TA_PINS, TA_PINS_NO, TA0_SEQUENCE, TA0_SEQUENCE_NO, TA_SEQUENCE_INDEX); break;
    case 1:  playSequence(TA_PINS, TA_PINS_NO, TA1_SEQUENCE, TA1_SEQUENCE_NO, TA_SEQUENCE_INDEX); break;
    case 2:  playSequence(TA_PINS, TA_PINS_NO, TA2_SEQUENCE, TA2_SEQUENCE_NO, TA_SEQUENCE_INDEX); break;
    case 3:  playSequence(TA_PINS, TA_PINS_NO, TA3_SEQUENCE, TA3_SEQUENCE_NO, TA_SEQUENCE_INDEX); break;
    case 4:  playSequence(TA_PINS, TA_PINS_NO, TA4_SEQUENCE, TA4_SEQUENCE_NO, TA_SEQUENCE_INDEX); break;
    case 5:  playSequence(TA_PINS, TA_PINS_NO, TA5_SEQUENCE, TA5_SEQUENCE_NO, TA_SEQUENCE_INDEX); break;
    case 6:  playSequence(TA_PINS, TA_PINS_NO, TA6_SEQUENCE, TA6_SEQUENCE_NO, TA_SEQUENCE_INDEX); break;
    case 7:  playSequence(TA_PINS, TA_PINS_NO, TA7_SEQUENCE, TA7_SEQUENCE_NO, TA_SEQUENCE_INDEX); break;
    case 8:  playSequence(TA_PINS, TA_PINS_NO, TA8_SEQUENCE, TA8_SEQUENCE_NO, TA_SEQUENCE_INDEX); break;
    case 9:  playSequence(TA_PINS, TA_PINS_NO, TA9_SEQUENCE, TA9_SEQUENCE_NO, TA_SEQUENCE_INDEX); break;
    case 10:  playSequence(TA_PINS, TA_PINS_NO, TA10_SEQUENCE, TA10_SEQUENCE_NO, TA_SEQUENCE_INDEX); break;
  }
}