Running out of ram, help please.

Hey guys. i am making a NFC queue system. but the problem is that every time i scan a NFC tag, my arduino runs out of ram very fast. i have made a 2d array, where i wanna store the names in the queue.

532
ok
free ram: 580        ****i start with 580.****
Entering reader
inside UID
Reading
wrote albert at line: 0
1. albert
free ram: 548 ****i scan one time, and it goes down****
Entering reader
inside UID
Reading
wrote aaaaaaaaaaaa at line: 1
1. albert
2. aaaaaaaaaaaa
free ram: 516 ****same here****
Entering reader
inside UID
Reading
wrote balbert at line: 2
1. albert
2. aaaaaaaaaaaa
3. balbert
free ram: 484 ****same here***
Entering reader
inside UID
Reading
wrote bbb at line: 3
1. albert
2. aaaaaaaaaaaa
3. balbert
4. bbb
free ram: 452 ***same here***
Entering reader

so i am gonna run out of ram way too quickly. why do i run out of ram? also, when i remove a name from the list, it still consumes ram.

Any help please? :slight_smile:

byte ENTRY_SIZE = 20;
int ENTRY_COUNT = 20;
byte index = 0;
byte linenumber = 0;
int lineToClear;
char inChar;
char name[20];
char nameList [20][20];
boolean lineNumberSetDirty;
#include <Mifare.h>
#include <NDEF.h>
#include <Wire.h>
#include <PN532_I2C.h>
#define IRQ   2
#define RESET 3
#define PAYLOAD_SIZE 224
PN532 * board = new PN532_I2C(IRQ, RESET);

Mifare mifare;
//init keys for reading classic
uint8_t Mifare::useKey = KEY_A;
uint8_t Mifare::keyA[6] = {
  0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7 };
uint8_t Mifare::keyB[6] = {
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
uint32_t Mifare::cardType = 0; //will get overwritten if it finds a different card

uint8_t payload[PAYLOAD_SIZE] = {
};

void setup(void) {
  Serial.begin(115200);

  board->begin();

  uint32_t versiondata = board->getFirmwareVersion();
  if (! versiondata) {
    Serial.println("error");
    while (1); // halt
  }
  Serial.print("5");
  Serial.println((versiondata>>24) & 0xFF, HEX); 

  if(mifare.SAMConfig()){
    Serial.println("ok");  
  }
  else{
    Serial.println("er");
  }   
  reader();
}

int freeRam () {
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}

void reader()
{
  printer();
  Serial.print("free ram: ");
  Serial.println(freeRam());
  delay(100);
  Serial.println("Entering reader");
  uint8_t * uid = mifare.readTarget();
  if(uid){
    Serial.println("inside UID");
    delay(100);
    mifare.readPayload(payload, PAYLOAD_SIZE);  
    Serial.println("Reading");
    FOUND_MESSAGE m = NDEF().decode_message(payload);

    for(int i = 0; i < PAYLOAD_SIZE; i++) {
      inChar = m.payload[i];
      name[i] = inChar;
      if(inChar == 0) {
        name[i] = 0;
        memset(payload, 0, PAYLOAD_SIZE);
        nameChecker();
        break;
      }

    }
  }
}

void printer()
{
    for(byte i = 0; i < ENTRY_COUNT; i++)
  { 
    if (nameList[i][0] != 0)
    {
      Serial.print(i+1);
      Serial.print(". ");
      Serial.println(nameList[i]);
    }
  }
  index = 0;
}

void nameChecker()
{
  delay(1000);
  for(byte i = 0; i < ENTRY_COUNT; i++)
  {
    delay(10);
    if(strcmp(nameList[i], name) == 0)
    {
      lineToClear = i;
      do
      {
        nameList[i][index] = 0;
        index++;
      } 
      while (index < ENTRY_SIZE);
      index = 0;
      lineNumberSetDirty = false;
      reader();
      break;
    }
  }
  writer();
}


void writer()
{
  delay(1000);
  {
    for(byte i = 0; i < ENTRY_SIZE; i++)
    {
      inChar = name[i];

      if (inChar == 0 ) 
      {
        nameList[linenumber][i] = 0; 
        linenumber++;
        lineNumberSetDirty = false;
        delay(100);
        break;
      } 
      else
      {
        nameList[linenumber][i] = inChar;
      }
    }
  }
  Serial.print("wrote ");
  Serial.print(nameList[linenumber-1]);
  Serial.print(" at line: ");
  Serial.println(linenumber-1);
  reader();
}


void loop() {
}

I think memory is allocated and not returned.
Which library and which example do you use an example for the Mifare ? Please copy a link in the text. Perhaps it is an old one.

A few small other things to optimize:

  • Use the 'F()' macro to reduce ram use when you print a string, like this : Serial.println(F("error"));
  • When you use a constant value, use the 'const' keyboard. Perhaps for keyA and keyB : const uint8_t Mifare::keyA[6] = { ....

Which Arduino board are you using and which version of the IDE? Also, what's the purpose of this line?

    while (index < ENTRY_SIZE);

If the code gets here, it seems like you'll spend a lot of time here since the loop body doesn't change anything.

Peter_n:
I think memory is allocated and not returned.
Which library and which example do you use an example for the Mifare ? Please copy a link in the text. Perhaps it is an old one.

A few small other things to optimize:

  • Use the 'F()' macro to reduce ram use when you print a string, like this : Serial.println(F("error"));
  • When you use a constant value, use the 'const' keyboard. Perhaps for keyA and keyB : const uint8_t Mifare::keyA[6] = { ....

Sorry, i am not that good at code yet. where do you wanna use that macro?
Also, the uint8_t mifares next to the defines, isn't my work. i got that from a nfc read example. :slight_smile:

Thanks alot for your time! :slight_smile:

what kind of Arduino do you have?

it is clear you loose 32 bytes per iteration.

I expect problem is in reader

void reader()
{
  printer();
  Serial.print("Entering reader: ");
  Serial.println(freeRam());
  delay(100);

  Serial.println(freeRam());
  uint8_t * uid = mifare.readTarget();  // <<<<<<<<<<<<<< HERE ?
  Serial.println(freeRam());

  if(uid){
    Serial.println("inside UID");
    delay(100);

    Serial.println("Reading payload");
    Serial.println(freeRam());
    mifare.readPayload(payload, PAYLOAD_SIZE);  
    FOUND_MESSAGE m = NDEF().decode_message(payload);   // <<<<<<<<<<<<<< HERE ?
    Serial.println(freeRam());

    for(int i = 0; i < PAYLOAD_SIZE; i++) {
      inChar = m.payload[i];
      name[i] = inChar;
      if (inChar == 0) {
        name[i] = 0;   // <<<<<<<<<<<<< already done in code above
        memset(payload, 0, PAYLOAD_SIZE);
        nameChecker();
        break;
      }
    }
  }
  Serial.print("Leaving reader: ");
  Serial.println(freeRam());
}

if there is memory allocated by mifare.readTarget() it is never freed.
so wrap the call with two freeram() and check if there is the leak

econjack:
Which Arduino board are you using and which version of the IDE? Also, what's the purpose of this line?

    while (index < ENTRY_SIZE);

If the code gets here, it seems like you'll spend a lot of time here since the loop body doesn't change anything.

I am using arduino uno r3. with ide 1.0.5-r2 (this is what you asked for right?)
Aight, so this was me trying out a theory, i wanted to see if i would get some ram back, if i null terminated all the places in the array, where the chars had to be. it didn't work. :confused:

void nameChecker()
{
  delay(1000);
  for(byte i = 0; i < ENTRY_COUNT; i++)
  {
    delay(10);
    if(strcmp(nameList[i], name) == 0)
    {
      lineToClear = i;
      do
      {
        nameList[i][index] = 0;
        index++;
      } 
      while (index < ENTRY_SIZE);  // means that i null terminate the whole string, if i scan a card, which is already ont he list. :)
      index = 0;
      lineNumberSetDirty = false;
      reader();
      break;
    }
  }
  writer();
}

robtillaart:
what kind of Arduino do you have?

it is clear you loose 32 bytes per iteration.

I expect problem is in reader

void reader()

{
 printer();
 Serial.print("Entering reader: ");
 Serial.println(freeRam());
 delay(100);

Serial.println(freeRam());
 uint8_t * uid = mifare.readTarget();  // <<<<<<<<<<<<<< HERE ?
 Serial.println(freeRam());

if(uid){
   Serial.println("inside UID");
   delay(100);

Serial.println("Reading payload");
   Serial.println(freeRam());
   mifare.readPayload(payload, PAYLOAD_SIZE);  
   FOUND_MESSAGE m = NDEF().decode_message(payload);   // <<<<<<<<<<<<<< HERE ?
   Serial.println(freeRam());

for(int i = 0; i < PAYLOAD_SIZE; i++) {
     inChar = m.payload[i];
     name[i] = inChar;
     if (inChar == 0) {
       name[i] = 0;   // <<<<<<<<<<<<< already done in code above
       memset(payload, 0, PAYLOAD_SIZE);
       nameChecker();
       break;
     }
   }
 }
 Serial.print("Leaving reader: ");
 Serial.println(freeRam());
}



if there is memory allocated by mifare.readTarget() it is never freed.
so wrap the call with two freeram() and check if there is the leak

Arduino uno r3. :slight_smile:

I think i did as you said. That gave me this.

532
ok
free ram: 522
starting first test
522
522
First test done
inside UID
Reading
starting second test
522
522
Second test done
wrote balbert at line: 0
1. balbert
free ram: 490
starting first test
490
490
First test done
inside UID
Reading
starting second test
490
490
Second test done
wrote bbb at line: 1
1. balbert
2. bbb
free ram: 458
starting first test
458
458
First test done
inside UID
Reading
starting second test
458
458
Second test done
wrote albert at line: 2
1. balbert
2. bbb
3. albert
free ram: 426
starting first test
426
426
First test done
inside UID
Reading
starting second test
426
426
Second test done
2. bbb
3. albert
free ram: 396
starting first test
396
void reader()
{
  printer();
  Serial.print("free ram: ");
  Serial.println(freeRam());
  delay(100);
   Serial.println("starting first test");
  Serial.println(freeRam());
  uint8_t * uid = mifare.readTarget();
  Serial.println(freeRam());
  Serial.println("First test done");
  
  if(uid){
    Serial.println("inside UID");
    delay(100);
    mifare.readPayload(payload, PAYLOAD_SIZE);  
    Serial.println("Reading");
    
    Serial.println("starting second test");
    Serial.println(freeRam());
    FOUND_MESSAGE m = NDEF().decode_message(payload);
    Serial.println(freeRam());
    Serial.println("Second test done");
    
    for(int i = 0; i < PAYLOAD_SIZE; i++) {
      inChar = m.payload[i];
      name[i] = inChar;
      if(inChar == 0) {
        memset(payload, 0, PAYLOAD_SIZE);
        nameChecker();
        break;
      }

    }
  }
}

void printer()
{
  for(byte i = 0; i < ENTRY_COUNT; i++)
  { 
    if (nameList[i][0] != 0)
    {
      Serial.print(i+1);
      Serial.print(". ");
      Serial.println(nameList[i]);
    }
  }
  index = 0;
}

Peter_n:
I think memory is allocated and not returned.
Which library and which example do you use an example for the Mifare ? Please copy a link in the text. Perhaps it is an old one.

A few small other things to optimize:

  • Use the 'F()' macro to reduce ram use when you print a string, like this : Serial.println(F("error"));
  • When you use a constant value, use the 'const' keyboard. Perhaps for keyA and keyB : const uint8_t Mifare::keyA[6] = { ....

I use this.

I do not see the output of these:

Serial.print("Entering reader: ");
Serial.println(freeRam());

and

Serial.print("Leaving reader: ");
Serial.println(freeRam());

So you can't be sure memory is lost in the reader()
For one call the memory seems to be stable

On second examination : it is probably a recursion problem:

namechecker() calls reader()

reader() calls namechecker()

if that happens often enough the stack explodes "

you need to redesign your application logic without the recursion...

robtillaart:
On second examination : it is probably a recursion problem:

namechecker() calls reader()

reader() calls namechecker()

if that happens often enough the stack explodes "

you need to redesign your application logic without the recursion...

Ah i see. i just don't know how. I mean. the reader will always have to call the namechecker, to see if the name already exists on the list, but if the name is already on the list, it will have to go back to the reader, and wait for another card.

is it better to do something like this?

void setup()
{
readerReady = 1
}

void loop()
{
if (readerReady == 1)
{
//Do reader code
nameCheckerReady = 1; //allow namechecker to run
readerReady = 0; //stop reader from running
}

if (nameCheckerReady == 1)
{
//do namechecker code
readerReady = 1; //alow reader to run
namecheckerReady = 0; //stop namechecker from running
}
}

is that better?

Ah i see. i just don't know how. I mean. the reader will always have to call the namechecker, to see if the name already exists on the list, but if the name is already on the list, it will have to go back to the reader, and wait for another card.

The key there is "go back to". That involves a return, NOT calling the function again.

while (index < ENTRY_SIZE);

Unless index is being changed volatilely somewhere, if this condition ever becomes true, it is game over for your program.

michinyon:

while (index < ENTRY_SIZE);

Unless index is being changed volatilely somewhere, if this condition ever becomes true, it is game over for your program.

It might be worth noting that that while statement is part of a do/while statement.