Trying to reduce dynamic memory use

Hi folks,

First, I'm really, really green at this, so thanks in advance for bearing with me. I'm using an Uno.

I'm building a simple RFID based music player for my son, and I've run into problems adding more than about 12 songs to my code, as it gives me an error stating that my global variables exceed the dynamic memory limit (2,974 out of 2,048 with the code below). My goal is to have about 25 tracks on here if possible.

Here's my code:

/*
  jukebox

  this is the firmware for the contact-free jukebox.
  that can play audio tracks depending on rfid tags.

  attached to the arduino is an adafruit waveshield
  and an id-12 rfid reader.

  created @ 8.1.12 by dominik grob (@ruedi)

  more information: http://tmblr.co/ZKZ-twHiQ_OK
*/

#include <FatReader.h>
#include <SdReader.h>
#include <avr/pgmspace.h>
#include "WaveUtil.h"
#include "WaveHC.h"

#include <SoftwareSerial.h>

//  audio settings
boolean PlayComplete = false;
long playnext_prevmillis = 0;
long playanother_time = 3000;

//  sdcard settings
SdReader card;
FatVolume vol;
FatReader root;
FatReader f;

WaveHC wave;

//  rfid settings
SoftwareSerial RFIDSerial(8, 6);

int RFIDResetPin = 7;
char RFIDtag[14];
int RFIDindex = 0;
boolean RFIDreading = false;

boolean debug = true;

//  define tag id and tracks
#define NUMTAGS 27
char audiotags[NUMTAGS][27] = {"0000EE1E6E9E",
                               "0000EECFC8E9",
                               "0000EECFA889",
                               "000048E1FD54",
                               "0000EB80A1CA",
                               "0000EC195EAB",
                               "0000EC1947B2",
                               "00004A201379",
                               "00004A1F590C",
                               "0000EEAF6322",
                               "000048888141",
                               "0000488860A0",
                               "000048A5CF22",
                               "000048A5CC21",
                               "000048572A35",
                               "00004A32B3CB",
                               "00004A32D2AA",
                               "000048578798",
                               "000048578897",
                               "0000488677B9",
                               "00004885FC31",
                               "0000EE3339E4",
                               "0000EECFCBEA",
                               "0000EECFC6E7",
                               "00004A1BDD8C"};
// make sure soundfile names are not longer then 8 chars (without filetype)
char audiofiles[NUMTAGS][27] = {"HELLO.wav",
                                "GOODBYE.wav",
                                "BIPPOLO.wav",
                                "BUTTER.wav",
                                "FTDOWN.wav",
                                "FTLIST.wav",
                                "FTSPRING.wav",
                                "FTTOM.wav",
                                "GUSTOV.wav",
                                "HORNBACK.wav",
                                "LUCKY.wav",
                                "MCBRIDE.wav",
                                "POLKA.wav",
                                "RABBIT.wav",
                                "RMAMBER.wav",
                                "RMRUBY.wav",
                                "RMSKY.wav",
                                "RMSTORM.wav",
                                "RMSUNNY.wav",
                                "SPOT.wav",
                                "STEAK.wav",
                                "STONE.wav",
                                "TAD.wav",
                                "TWINBABY.wav",
                                "RMINKY.wav"};

void setup() {
  Serial.begin(9600);
  putstring_nl("debug");
  putstring_nl("----------------");

  putstring("Free RAM: ");
  Serial.println(freeRam());

  //  set output pins for DAC control
  //  pins are defined in the library
  pinMode(2, OUTPUT);
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(5, OUTPUT);

  // pin13 LED
  pinMode(13, OUTPUT);

  if (!card.init()) {
    putstring_nl("Card init. failed!");
    sdErrorCheck();
    while(1);
  }

  // enable optimize read
  card.partialBlockRead(true);

  // fat partition?
  uint8_t part;
  for (part = 0; part < 5; part++) {
    if (vol.init(card, part))
      break;
  }
  if (part == 5) {
    putstring_nl("No valid FAT partition!");
    sdErrorCheck();
    while(1);
  }

  // show infos
  putstring("Using partition ");
  Serial.print(part, DEC);
  putstring(", type is FAT");
  Serial.println(vol.fatType(),DEC);

  if (!root.openRoot(vol)) {
    putstring_nl("Can't open root dir!");
    while(1);
  }

  putstring_nl("> sdcard ready");

  // rfid setup
  pinMode(RFIDResetPin, OUTPUT);
  digitalWrite(RFIDResetPin, HIGH);
  RFIDSerial.begin(9600);

  putstring_nl("> rfid ready");

  // play startup chime
  delay(500);  // avoid loudspeaker click noise
  playcomplete("CHIME.wav");
}

void loop() {
  RFIDindex = 0;

  //  rfid data?
  while(RFIDSerial.available()) {
    int readByte = RFIDSerial.read();

    if(readByte == 2) RFIDreading = true;
    if(readByte == 3) RFIDreading = false;

    if(RFIDreading && readByte != 2 && readByte != 10 && readByte != 13){
      RFIDtag[RFIDindex] = readByte;
      RFIDindex++;
    }
  }

  //  check tag and play track if tag id found
  checkTag(RFIDtag);
  //  prepare for next read
  clearTag(RFIDtag);
  resetReader();
}

void playcomplete(char *name) {
  playfile(name);
  while (wave.isplaying) {
    // playing
  }
}

void playfile(char *name) {
  if (wave.isplaying) {
    wave.stop();
  }
  if (!f.open(root, name)) {
    putstring("Couldn't open file "); Serial.println(name); return;
  }
  if (!wave.create(f)) {
    putstring_nl("Not a valid WAV"); return;
  }

  wave.play();
}

void resetReader() {
  digitalWrite(RFIDResetPin, LOW);
  digitalWrite(RFIDResetPin, HIGH);
  delay(150);
}

void clearTag(char one[]) {
  for(int i = 0; i < strlen(one); i++){
    one[i] = 0;
  }
}

void checkTag(char tag[]) {
  if(strlen(tag) == 0) return;

  if(debug) {
    putstring("rfid tag id: ");
    for(int d = 0; d < 12; d++) {
      Serial.print(tag[d]);
    }
    Serial.println();
  }

  boolean matching = true;

  //  compare tag id
  for(int a = 0; a < NUMTAGS; a++) {
    matching = true;
    for(int c = 0; c < 12; c++) {
      if(tag[c] != audiotags[a][c]) {
        matching = false;
        break;
      }
    }

    //  in case of a match play the track
    if(matching) {
      putstring("playing: "); Serial.println(audiofiles[a]);
      delay(500);  // avoid loudspeaker click noise
      if(PlayComplete) {
        digitalWrite(RFIDResetPin, LOW);
        playcomplete(audiofiles[a]);
      } else {
        unsigned long playnext_currentmillis = millis();
        // wait with playing another audio track
        if(playnext_currentmillis - playnext_prevmillis > playanother_time) {
          playfile(audiofiles[a]);
          playnext_prevmillis = playnext_currentmillis;
        }
      }
      break;
    }
  }
}


// ---------------------------
// debug functions
// ---------------------------
int freeRam(void) {
  extern int  __bss_end;
  extern int  *__brkval;
  int free_memory;
  if((int)__brkval == 0) {
    free_memory = ((int)&free_memory) - ((int)&__bss_end);
  }
  else {
    free_memory = ((int)&free_memory) - ((int)__brkval);
  }
  return free_memory;
}

void sdErrorCheck(void) {
  if (!card.errorCode()) return;
  putstring("\n\rSD I/O error: ");
  Serial.print(card.errorCode(), HEX);
  putstring(", ");
  Serial.println(card.errorData(), HEX);
  while(1);
}

Is there anywhere in there that I can reduce the memory footprint so that I can have more than a dozen or so files linked? Note that I don't really have a way (that I know of) to reduce the length of the RFID codes under "audiotags", because I only have a reader, not a writer. I could change the "audiofiles" names to anything.

Thanks very much for any help you can give, I really appreciate it.

The first thing you could do is move those big character arrays into flash program memory.

Take a look at "PROGMEM". Some info here:- PROGMEM

That would save you almost 1.5KB of SRAM.

On an aside, this should be 'unsigned long', not 'long':-

long playnext_prevmillis = 0;

Fantastic, thanks!

I must apologize, though - I'm having trouble figuring out how to use that. Would I just be adding the

const dataType variableName[] PROGMEM =

in the place of my current

char audiotags[NUMTAGS][27]

Thanks very much for bearing with my inexperience at this!

elliotlevin:
Fantastic, thanks!

I must apologize, though - I'm having trouble figuring out how to use that. Would I just be adding the

const dataType variableName[] PROGMEM =

in the place of my current

char audiotags[NUMTAGS][27]

Thanks very much for bearing with my inexperience at this!

You need to read, study and understand the whole page that I linked. Take your time, and even write a couple of small test programs.
You need to use specialised methods of reading the data from flash memory, and a specialised method of storing an array of strings. It's all explained.

Pay particular attention to the "Arrays of strings" section.

I could only repeat what's written on that page.

You've got a load of strings in your prints that would benefit from the F() macro too.
If you've got a memory card, why not store the indices on that?

char audiotags[NUMTAGS][27] = {"0000EE1E6E9E",

...
...

How big is a tag? 12 characters max? You can change the 27 to 13.

char audiotags[NUMTAGS][13] = {"0000EE1E6E9E",
...
...

Same for filenames; 8-dot-3 notation so max 12 characters; again change the second dimension to 13

That will reduce the memory usage for those arrays by over 50%.