I'm building a final project for a class that involves playing an audio file of a wave shield every time I scan an amiibo with an RFID scanner. I'm a pretty new programmer, so I'm not very familiar with the how to best reduce DRAM usage. As such, I got the following error when I attempted to run my most recent iteration of code:
Global variables use 2801 bytes (136%) of dynamic memory, leaving -753 bytes for local variables. Maximum is 2048 bytes.
Here is my code:
////// WAVE SHIELD HEADERS //////
#include <FatReader.h>
#include <SdReader.h>
#include <avr/pgmspace.h>
#include "WaveUtil.h"
#include "WaveHC.h"
/////////////////////////////////
/////// LCD HEADERS /////////////
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,16,2); // set the LCD address to 0x27 for a 16 chars and 2 line display
//////////////////////////////////////////
////////// RFID HEADERS /////////////////
#include <SPI.h>
#include <Adafruit_PN532.h>
#define PN532_IRQ (6)
#define PN532_RESET (3) // Not connected by default on the NFC Shield
Adafruit_PN532 nfc(PN532_IRQ, PN532_RESET);
/////// various declarations /////////
uint8_t GLOBALUID[7];
//////////////////////////// WAVE SHIELD PORTION OF CODE ///////////////////////////////////
SdReader card; // This object holds the information for the card
FatVolume vol; // This holds the information for the partition on the card
FatReader root; // This holds the information for the filesystem on the card
FatReader f; // This holds the information for the file we're play
WaveHC wave; // This is the only wave (audio) object, since we will only play one at a time
#define DEBOUNCE 100 // button debouncer
// this handy function will return the number of bytes currently free in RAM, great for debugging!
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);
}
/////// MAIN SETUP /////////
void setup() {
// set up serial port
Serial.begin(115200);
// Set the output pins for the DAC control in the wave shield.
pinMode(2, OUTPUT);
pinMode(3, OUTPUT);
pinMode(4, OUTPUT);
pinMode(5, OUTPUT);
// Set button outputs
pinMode(A0, INPUT);
pinMode(A1, INPUT);
// Set random seed for picking amiibo
randomSeed(analogRead(A2));
// initialize lcd
lcd.init();
lcd.init();
lcd.backlight();
lcd.setCursor(0,0);
if (!card.init()) {
putstring_nl("Card init. failed!");
sdErrorCheck();
while(1);
}
// enable optimize read - some cards may timeout. Disable if you're having problems
card.partialBlockRead(true);
// look for a 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);
}
// tell the user about what was found
putstring("Using partition ");
Serial.print(part, DEC);
putstring(", type is FAT");
Serial.println(vol.fatType(),DEC); // FAT16 or FAT32?
// Try to open the root directory
if (!root.openRoot(vol)) {
putstring_nl("Can't open root dir!");
while(1);
}
}
//////////// END OF WAVE SHIELD ///////////////
////////////// FUNCTION FOR SELECTING AUDIO FILES ////////////
void playSoundClip(){
char charUID[7];
for(int i = 0; i < 7; i++)
charUID[i] = GLOBALUID[i];
Serial.println();
/*
{"Mario", { 4, 79, 119, 42, 55, 60, 128 },"MARIO"}, //0
{"Meta Knight", { 4, 243, 209, 58, 117, 76, 128},"MK"}, //1
{"Inkling", { 4, 84, 47, 218, 87, 73, 128 },"INK"}, //2
{"Donkey Kong", { 4, 244, 122, 98, 249, 61, 128 },"DK"}, //3
{"Lucas", { 4, 7, 52, 202, 178, 73, 129 },"LUCAS"}, //4
{"Duck Hunt", { 4, 40, 173, 146, 84, 73, 128 },"DUCKH"}, //5
{"Ocarina of Time Link", { 4 163 57 114 221 76 128 },"OOTL"}, //6
{"Breath of The Wild Link", ( 4 133 61 74 120 76 128 },"BOTWL"}, //7
{"8-bit Link", { 4 192 150 130 221 76 128 },"8BITL"}, //8
{"Toon Link", { 4, 89, 88, 226, 161, 62, 128 },"TOONL"}, //9
{"Mr. Game and Watch", { 4, 134, 32, 154, 85, 73, 128 },"GANDW"}, //10
{"Falco", { 4, 23, 139, 178, 17, 74, 129 },"FALCO"}, //11
{"King Dedede", { 4, 108, 188, 242, 232, 73, 128 },"DEDEDE"}, //12
{"Mewtwo", { 4, 241, 126, 202, 87, 73, 128 },"MEWTWO"}, //13
{"Captain Falcon", { 4, 11, 204, 178, 124, 72, 129 },"CAPF"}, //14
{"ROB", { 4, 40, 208, 50, 3, 73, 128 },"ROB"}, //15
{"PAC-MAN", { 4, 45, 26, 2, 114, 64, 129 },"PACMAN"}, //16
{"Cloud", { 4, 107, 128, 26, 114, 64, 128 },"CLOUD"}, //17
{"Ness", { 4, 107, 128, 26, 114, 64, 128 },"NESS"}, //18
{"Ryu", { 4, 109, 252, 234, 33, 75, 128 },"RYU"} //19
*/
uint8_t amiiboNo = 0;
if(strcmp(charUID,"479119425560128"))
amiiboNo = 0; //Mario
else if(strcmp(charUID,"42432095811776128"))
amiiboNo = 1; //Meta Knight
else if(strcmp(charUID,"484472188773128"))
amiiboNo = 2; //Inkling
else if(strcmp(charUID,"42441229824961128"))
amiiboNo = 3;// Donkey Kong
else if(strcmp(charUID,"475220217873129"))
amiiboNo = 4;// Lucas
else if(strcmp(charUID,"4401731468473128"))
amiiboNo = 5;// Duck Hunt
else if(strcmp(charUID,"41635711422176128"))
amiiboNo = 6;// Ocarina of Time Link
else if(strcmp(charUID,"4133617412076128"))
amiiboNo = 7;// Breath of The Wild Link
else if(strcmp(charUID,"419215013022176128"))
amiiboNo = 8;// 8-bit link
else if(strcmp(charUID,"4898822616162128"))
amiiboNo = 9;// toon link
else if(strcmp(charUID,"4134321548573128"))
amiiboNo = 10;// Mr. Game and Watch
else if(strcmp(charUID,"4231391781774129"))
amiiboNo = 11;// Falco
else if(strcmp(charUID,"410818824223273128"))
amiiboNo = 12;// king dedede
else if(strcmp(charUID,"42411262028773128"))
amiiboNo = 13;// Mewtwo
else if(strcmp(charUID,"41120417812472129"))
amiiboNo = 14;// Captain Falcon
else if(strcmp(charUID,"44020850373128"))
amiiboNo = 15;// ROB
else if(strcmp(charUID,"44526211464129"))
amiiboNo = 16;// Pacman
else if(strcmp(charUID,"424913721823376128"))
amiiboNo = 17;// Cloud
else if(strcmp(charUID,"41071282611464128"))
amiiboNo = 18;// Ness
else if(strcmp(charUID,"41092522343375128"))
amiiboNo = 19;// Ryu
Serial.println(amiiboNo);
uint8_t clipNo = random(3);
switch(amiiboNo){
case 0:
if(clipNo == 0)
playComplete("MARIO01.WAV");
else if(clipNo == 1)
playComplete("MARIO02.WAV");
else if(clipNo == 2)
playComplete("MARIO03.WAV");
break;
case 1:
if(clipNo == 0)
playComplete("MK01.WAV");
else if(clipNo == 1)
playComplete("MK02.WAV");
else if(clipNo == 2)
playComplete("MK03.WAV");
break;
case 2:
if(clipNo == 0)
playComplete("INK01.WAV");
else if(clipNo == 1)
playComplete("INK02.WAV");
else if(clipNo == 2)
playComplete("INK03.WAV");
break;
case 3:
if(clipNo == 0)
playComplete("DK01.WAV");
else if(clipNo == 1)
playComplete("DK02.WAV");
else if(clipNo == 2)
playComplete("DK03.WAV");
break;
case 4:
if(clipNo == 0)
playComplete("LUCAS01.WAV");
else if(clipNo == 1)
playComplete("LUCAS02.WAV");
else if(clipNo == 2)
playComplete("LUCAS03.WAV");
break;
case 5:
if(clipNo == 0)
playComplete("DUCKH01.WAV");
else if(clipNo == 1)
playComplete("DUCKH02.WAV");
else if(clipNo == 2)
playComplete("DUCKH03.WAV");
break;
case 6:
if(clipNo == 0)
playComplete("OOT01.WAV");
else if(clipNo == 1)
playComplete("OOT02.WAV");
else if(clipNo == 2)
playComplete("OOT03.WAV");
break;
case 7:
if(clipNo == 0)
playComplete("BOTWL01.WAV");
else if(clipNo == 1)
playComplete("BOTWL02.WAV");
else if(clipNo == 2)
playComplete("BOTWL03.WAV");
break;
case 8:
if(clipNo == 0)
playComplete("8BITL01.WAV");
else if(clipNo == 1)
playComplete("8BITL02.WAV");
else if(clipNo == 2)
playComplete("8BITL03.WAV");
break;
case 9:
if(clipNo == 0)
playComplete("TOONL01.WAV");
else if(clipNo == 1)
playComplete("TOONL02.WAV");
else if(clipNo == 2)
playComplete("TOONL03.WAV");
break;
case 10:
if(clipNo == 0)
playComplete("GANDW01.WAV");
else if(clipNo == 1)
playComplete("GANDW02.WAV");
else if(clipNo == 2)
playComplete("GANDW03.WAV");
break;
case 11:
if(clipNo == 0)
playComplete("FALCO01.WAV");
else if(clipNo == 1)
playComplete("FALCO02.WAV");
else if(clipNo == 2)
playComplete("FALCO03.WAV");
break;
case 12:
if(clipNo == 0)
playComplete("DEDEDE01.WAV");
else if(clipNo == 1)
playComplete("DEDEDE02.WAV");
else if(clipNo == 2)
playComplete("DEDEDE03.WAV");
break;
case 13:
if(clipNo == 0)
playComplete("MEWTWO01.WAV");
else if(clipNo == 1)
playComplete("MEWTWO02.WAV");
else if(clipNo == 2)
playComplete("MEWTWO03.WAV");
break;
case 14:
if(clipNo == 0)
playComplete("CAPF01.WAV");
else if(clipNo == 1)
playComplete("CAPF02.WAV");
else if(clipNo == 2)
playComplete("CAPF03.WAV");
break;
case 15:
if(clipNo == 0)
playComplete("ROB01.WAV");
else if(clipNo == 1)
playComplete("ROB02.WAV");
else if(clipNo == 2)
playComplete("ROB03.WAV");
break;
case 16:
if(clipNo == 0)
playComplete("PACMAN01.WAV");
else if(clipNo == 1)
playComplete("PACMAN02.WAV");
else if(clipNo == 2)
playComplete("PACMAN03.WAV");
break;
case 17:
if(clipNo == 0)
playComplete("CLOUD01.WAV");
else if(clipNo == 1)
playComplete("CLOUD02.WAV");
else if(clipNo == 2)
playComplete("CLOUD03.WAV");
break;
case 18:
if(clipNo == 0)
playComplete("NESS01.WAV");
else if(clipNo == 1)
playComplete("NESS02.WAV");
else if(clipNo == 2)
playComplete("NESS03.WAV");
break;
case 19:
if(clipNo == 0)
playComplete("RYU01.WAV");
else if(clipNo == 1)
playComplete("RYU02.WAV");
else if(clipNo == 2)
playComplete("RYU03.WAV");
break;
}
}
//////// MAIN LOOP /////////
void loop() {
lcd.print("Scan an");
lcd.setCursor(0,1);
lcd.print("amiibo!");
delay(3000);
uint8_t * ID{};
waitForRFID();
playSoundClip();
}
//////////////// MORE WAVE SHIELD ///////////////////////
// Plays a full file from beginning to end with no pause.
void playComplete(char *name) {
// call our helper to find and play this name
playfile(name);
while (wave.isplaying) {
// do nothing while its playing
}
// now its done playing
}
void playfile(char *name) {
// see if the wave object is currently doing something
if (wave.isplaying) {// already playing something, so stop it!
wave.stop(); // stop it
}
// look in the root directory and open the file
if (!f.open(root, name)) {
putstring("Couldn't open file "); Serial.println(name); return;
}
// OK read the file and turn it into a wave object
if (!wave.create(f)) {
putstring_nl("Not a valid WAV"); return;
}
// ok time to play! start playback
wave.play();
}
/////////////// END OF WAVE SHIELD //////////////////////
//////////////// RFID FUNCTION /////////////////////////
uint8_t* waitForRFID(){
nfc.SAMConfig();
Serial.println("Waiting for an amiibo ...");
uint8_t success;
uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 };
uint8_t uidLength;
uint8_t testuid[7];
while(true){
if (nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength)){
Serial.print("Hex UID: ");
nfc.PrintHex(uid, uidLength);
Serial.print("Octal UID: { ");
for(int i = 0; i < uidLength; i++){
Serial.print(uid[i]);
GLOBALUID[i] = uid[i];
if (i != uidLength - 1)
Serial.print(", ");
}
Serial.println(" }");
Serial.print("GLOBALUID: ");
for(int i = 0; i < 7; i++)
Serial.print(GLOBALUID[i]);
Serial.println();
Serial.println();
}
return testuid;
}
}
//////////////// END OF RFID FUNCTION /////////////////
My first guess for what may be taking up the most memory is the switch statement. When I uncomment that, the memory usage jumps from 90% to over 130%. I've tried other ways to get this code to work, like concatenating text, but this is the only way that seemed to work reliably, until I ran out of memory.
I'm also using a lot of libraries to help use the shields and LCD I have attached to the Uno. I couldn't get rid of those libraries without compromising the project.
Other than that, I don't know what to do. If anyone has any suggestions for condensing the code down, I'd really appreciate any advice you have to offer.
Thanks.