"Stability problems": how likely?

How serious are message like this on my UNO?

Sketch uses 10722 bytes [33%) of program Storage Space. Maximum is 32256
Global variables use 1638 bytes (79%) of dynamic memory, leaving 410 bytes for local variables. Maximum is 2048 bytes.
Low memory available, stability problems may occur.

I think they arise whenever my global variables get over 75% of dynamic memory. That seems a surprisingly low bar? Redesigning the project would be challenging, but I don't want to press on if unexpected failures are reasonably likely.

Hard to say without seeing code
Dynamic allocations could be a factor.

It's a warning. Most frequently if you are using Strings then you are at a higher risk of running out of RAM during runtime.

Show your code.

every function call requires some stack space which consumes available memory, the 21% of RAM, as well as any variables allocated within the function.

it's actually more efficient to allocate larger amounts of variables (i.e. buffers) on the stack if they are only needed during the execution of that sub-function, especially if there are other sub-functions that have similar needs and are not nested calls. this way the RAM is reused for different buffers.

this can be bad if a function allocates a lot of stack variables and then calls (nested) another function that also allocates a lot of stack space.

Code below as requested:

/*
  Wed 22 Feb 2023 2037:
  Random mode OK. Normal mode OK. Volume,Toggle etc now working
  Still working on gettin Pause/Play working reliably.
  A few constants and variables no longer needed; awaits cleanup.

  "Sketch uses 10722 bytes (33%) of program storage space. Maximum is 32256 bytes.
  Global variables use 1638 bytes (79%) of dynamic memory, leaving 410 bytes for local variables. Maximum is 2048 bytes.
  Low memory available, stability problems may occur."

*/

//#include "Arduino.h" // Redundant
#include "SoftwareSerial.h"
#include "DFRobotDFPlayerMini.h"
#include "Toggle.h" // (Author David Lloyd) To use multiple button presses

// PINS
// 0 Unused
// 1 Unused
// 2 Spare buttons for experiment.

const byte pausePin = 3; // Toggles Pause/Play (WAS 3)
const byte yellowLedPin = 4;
//const byte ledPin = 5; // For code that seems to ensure starting on powerup
const byte previousPin = 6; // Previous
const byte nextPin = 7; // Next
const byte volupPin = 8; //Vol up
const byte voldownPin = 9; // Vol down
// 10 = Rx
// 11 = Tx
const byte BUSY_PIN = 12;
// 13 Unused & built-in LED
// A0 Unused
// A1 Unused

// CONSTANTS
const int  btnGrpDelay = 500; // Have used 500, 1000, 2000, 50, 10, 5, 0
const byte folderPin = A5; // For Toggle library
const byte jumpTenBackPin = A3; // Jump 10 back
const byte jumpTenFwdPin = A2; // Jump 10 forward
const byte modePin = A4; // SWITCH, not button, HIGH = Normal, LOW (sw to right) = Random

// VARIABLES
bool busyPinState = HIGH;
int  currentTrack; // Used in bit array method
byte fldr;
byte folderCode; // For Toggle library
int  folderSize;
int  i;
bool mode;
byte numFldrs = 26;
bool pauseBtnState = 1; // H = Not pressed
bool paused = false;
bool pauseState = 0; // Not paused
bool prevBusyPinState = LOW;
bool prevMode;
int  randNumberF;
int  randomTrack; // Used in bit array method
int  randTrk;
int  track;
int  trackCount = 1651;
unsigned long lastPlayTime = 0;
unsigned long busyCheckDelay = 300; // 600 300 1001000 200

// ARRAYS
// For 26 folder special, short content, e.g msd 'S6'
int trackArray[254]; // Max possible tracks in any folder
const int cumulTracks[26] {20, 24, 26, 30, 31, 35, 38, 42, 79, 105,
        183, 435, 506, 575, 760, 784, 865, 1027, 1212, 1289, 1380, 1415,
        1452, 1495, 1568, 1651
};
const byte folderSizes[26] {20, 4, 2, 4, 1, 4, 3, 4,  37, 26, 78,
        252, 71, 69, 185, 24, 81, 162, 185, 77, 91, 35, 37, 43, 73,
        83
};

// 'Special Short' content for bit array testing with 1651 short tracks; (allow 210 bytes for bit array)
// Full project has 2816 tracks, so array will need say 360 bytes
// Which look like a show stopper because of memory shortage
byte playedTracks[210];

SoftwareSerial mySoftwareSerial(10, 11);
DFRobotDFPlayerMini myDFPlayer;
void printDetail(uint8_t type, int value);
Toggle folder(folderPin); // For Toggle library

//   _____ ______ _______ _    _ _____
//  / ____|  ____|__   __| |  | |  __ \ 
// | (___ | |__     | |  | |  | | |__) |
//  \___ \|  __|    | |  | |  | |  ___/
//  ____) | |____   | |  | |__| | |
// |_____/|______|  |_|   \____/|_|
void setup()
{
  // pinMode(bluLedPin, OUTPUT); // D5 (temp blue LED), now redundant?
  pinMode(folderPin, INPUT_PULLUP); // A5 for Toggle
  pinMode(jumpTenBackPin, INPUT_PULLUP); // A3
  pinMode(jumpTenFwdPin, INPUT_PULLUP); // A2
  pinMode(modePin, INPUT_PULLUP); // A4
  pinMode(nextPin, INPUT_PULLUP); // D7
  pinMode(pausePin, INPUT_PULLUP); // D3
  pinMode(previousPin, INPUT_PULLUP); // D6
  pinMode(voldownPin, INPUT_PULLUP); // D9
  pinMode(volupPin, INPUT_PULLUP); // D8
  // pinMode(yellowLedPin, OUTPUT); // D4 (temp yellow LED)

  mySoftwareSerial.begin(9600);

  const char * compileFilename = __FILE__;
  Serial.begin(115200);
  delay(200);
  Serial.println(F("=============================================="));
  Serial.println( F("Code running comes from file ") );
  Serial.print("Full path : "); Serial.println(compileFilename);
  const char * lastSeparator = strrchr(compileFilename, '\\');
  if (lastSeparator  != nullptr)
  {
    Serial.print(F("File name : "));
    Serial.println(lastSeparator);
  }
  Serial.print( F("Compiled: ") );
  Serial.print( F(__DATE__) );
  Serial.print( F(" ") );
  Serial.println( F(__TIME__) );

  folder.begin(folderPin); // Button being used for Toggle library

  Serial.println(F("Initializing DFPlayer ... (May take 3~5 seconds)"));
  delay(1000);

  if (!myDFPlayer.begin(mySoftwareSerial, true, false)) // My edit
  {
    Serial.println(F("Unable to begin:"));
    Serial.println(F("1.Please recheck the connection!"));
    Serial.println(F("2.Please insert the SD card!"));
    while (true);
  }
  Serial.println(F("DFPlayer Mini online."));
  myDFPlayer.setTimeOut(1000);
  //----Set volume----
  myDFPlayer.volume(15);
  delay(500);

  randomSeed(analogRead(6) + analogRead(7));
  chooseRandFolder(); // Function
  createTrackArray(); // Function.
  //  For possible use directly after power up
  myDFPlayer.stop(); // Needed?
  delay(100);
  i = 0;

  // MODE SELECTION from 1P2W switch on A4
  mode = digitalRead(modePin); // 1 = Normal, 0 = Random
  prevMode = mode; // May not be used?
  Serial.print(F("Setup: switch mode = "));
  Serial.print(mode);
  Serial.print(F("\tprevMode = "));
  Serial.println(prevMode);
  Serial.println(F("End setup ---------------------------------"));
} // End setup

//   _      ____   ____  _____
//  | |    / __ \ / __ \|  __ \ 
//  | |   | |  | | |  | | |__) |
//  | |   | |  | | |  | |  ___/
//  | |___| |__| | |__| | |
//  |______\____/ \____/|_|

void loop()
{

  // Check BUSY pin
  if (millis() - lastPlayTime >= 300) // 300 1000
  {
    lastPlayTime = millis();
    busyPinState = digitalRead(BUSY_PIN);

    // If the BUSY pin goes high (i.e., track is finished), play the next track
    if (busyPinState == HIGH && prevBusyPinState == LOW)
    {
      if (mode == 0) // Random using bit array method
      {
        randomTrack = random(0, trackCount - 1); // Raw
        int byteIndex = randomTrack / 8;
        int bitIndex = randomTrack % 8;

        if (bitRead(playedTracks[byteIndex], bitIndex))
        {
          Serial.print(randomTrack);
          Serial.println(" would be a repetition");
          return;
        }

        myDFPlayer.play(randomTrack);
        bitWrite(playedTracks[byteIndex], bitIndex, 1); // mark track as played
        //        Serial.print("raw randomTrack = ");
        randToFT();

      }
      else if (mode == 1)
      {
        playTrackArray();
      }
    }
    prevBusyPinState = busyPinState;
  } // End millis & busy check
  //  prevBusyPinState = busyPinState;
  //---------------------------------------------------
  folder.poll(); // folder assigned to folderPin, for Toggle library
  folderCode = folder.pressCode(1); // print: () on () off . For Toggle library
  //---------------------------------------------------
  actionCases(); // Based on Toggle library (number of actions = folder size)
  //---------------------------------------------------
  // VOLUME
  if (digitalRead(volupPin) == LOW)
  {
    myDFPlayer.volumeUp();
    delay(50); //
  }
  if (digitalRead(voldownPin) == LOW)
  {
    myDFPlayer.volumeDown();
    delay(50);
  }
  //  //---------------------------------------------------
  // NEXT
  if (digitalRead(nextPin) == LOW) // D7
  {
    // Thu 2 Feb 2023 1125 try this
    if (mode == 0)
    {
      mode = 1;
      myDFPlayer.stop();
      delay(30);
    }

    myDFPlayer.next();
    delay(400); // 400
  }
  //---------------------------------------------------
  // PREVIOUS
  if (digitalRead(previousPin) == LOW) // D6
  {
    myDFPlayer.previous();
    delay(400); // 200 failed, 250 & 300 OK
  }
  //---------------------------------------------------
  // PAUSE/PLAY
  pauseBtnState = digitalRead(pausePin);
  //  Serial.print(F("pauseState = "));
  //  Serial.println(pauseBtnState);

  if (pauseBtnState == LOW)
  {
    myDFPlayer.pause();
    //    myDFPlayer.stop();
    pauseState = !pauseState;
    if (pauseState == 0)
    {
      Serial.println(F("Continue..."));
      myDFPlayer.start();
      delay(400); // 1000
    }
    if (pauseState == 1)
    {
      Serial.println(F("Music Paused!"));
      myDFPlayer.pause();
      delay(400); // 1000
    }
  }
  //---------------------------------------------------
  // JUMP 10 FORWARD
  if (digitalRead(jumpTenFwdPin) == LOW)
  {
    Serial.print(F("trackArray[i] = "));
    Serial.println(trackArray[i]);
    jumpTenFwd();
  }
  //---------------------------------------------------
  // JUMP 10 BACK
  if (digitalRead(jumpTenBackPin) == LOW)
  {
    Serial.print(F("trackArray[i] = "));
    Serial.println(trackArray[i]);
    jumpTenBack();
  }
  //---------------------------------------------------

} // End loop
//  ______ _    _ _   _  _____ _______ _____ ____  _   _  _____
// |  ____| |  | | \ | |/ ____|__   __|_   _/ __ \| \ | |/ ____|
// | |__  | |  | |  \| | |       | |    | || |  | |  \| | (___
// |  __| | |  | | . ` | |       | |    | || |  | | . ` |\___ \ 
// | |    | |__| | |\  | |____   | |   _| || |__| | |\  |____) |
// |_|     \____/|_| \_|\_____|  |_|  |_____\____/|_| \_|_____/

void randomWork()  // Random mode processing (bit array)
{
  randomTrack = random(0, trackCount - 1); // Raw
  //    Serial.print(F("randomTrack = "));
  //    Serial.println(randomTrack);

  int byteIndex = randomTrack / 8;
  int bitIndex = randomTrack % 8;

  if (bitRead(playedTracks[byteIndex], bitIndex))
  {
    Serial.print(randomTrack);
    Serial.println(F(" would be a repetition"));
    return;
  }

  myDFPlayer.play(randomTrack);
  bitWrite(playedTracks[byteIndex], bitIndex, 1); // mark track as played
  //        Serial.print("raw randomTrack = ");
  randToFT();
}
// --------------------

void normalWork()
{
  playTrackArray();
}
// --------------------
// Moved random folder choice from setup(); RUN in createTrackArray()
void chooseRandFolder()
{
  randNumberF = random(1, 27); // Choose random folder 1-26
  //   Temporarily override, to start with specific folder
  //  randNumberF = 19;
  folderSize = folderSizes[randNumberF - 1];
  Serial.print(F("randNumberF = "));
  Serial.print(randNumberF);
  Serial.print(F("\t"));
  Serial.print(F("folderSize = "));
  Serial.println(folderSize);
}
// --------------------

void createTrackArray() // Used in setup() & Cases
{
  //  chooseRandFolder();
  // When folder size is known, an array of its original track
  // numbers can be created, e.g from 1 to 26
  for (int i = 0; i < folderSize; i++)
  {
    trackArray[i] = i + 1;
  }
  // Randomise (shuffle) the folder tracks
  for (int i = 0; i < folderSize; i++)
  {
    int pos = random(folderSize);
    int t = trackArray[i];
    trackArray[i] = trackArray[pos];
    trackArray[pos] = t;
  }
  i = 0;
} // End createTrackArray()
// --------------------

void playTrackArray()
{
  // Only for Normal mode
  if (mode == 1)
  {
    // module must have just stopped being busy.
    if ( ( busyPinState != prevBusyPinState ) && ( busyPinState == HIGH ) )
    {
      //      prevBusyPinState = busyPinState; // Reset
      // When i > folder range
      if (i != folderSize) // One of folder's tracks
      {
        Serial.print(F("Playing ("));
        Serial.print(randNumberF);
        Serial.print(F(", "));
        Serial.print(trackArray[i]);
        Serial.println(")");

        myDFPlayer.playFolder( randNumberF , trackArray[i] );
        //        lastPlayTime = millis(); // Used elsewhere in loop() to
        // determine busy state at frequent intervals
        i++; // Increment index
      } // End full range processing

      // Unsure if 'else' was needed below as it works OK without
      if (i == folderSize) // So just gone over range
      {
        // Choose a new folder.
        randNumberF = random(1, 27); // Folders 1 to  26
        folderSize = folderSizes[randNumberF - 1];
        createTrackArray();
        delay(1000); // 1000 but lower values may also work
      } // End special condition
    }
  }
  prevBusyPinState = busyPinState; // Reset
}

// --------------------
void getRaw() // Gets raw from DFR (currentTrack) then calculates
// fldr and track.
{
  currentTrack = myDFPlayer.readCurrentFileNumber();
  delay(200); // 2000
  // Folder 1 code is special
  if (currentTrack <= cumulTracks[0]) // It's in folder 1
  {
    fldr = 1;
    track = currentTrack;
  }
  // Now test other ranges, e.g. for 26 folders test against
  // cumulTracks[] elements [1] to [25] (array indexes start at 0).
  for (int i = 1; i < numFldrs - 1; i++)
  {
    if (currentTrack <= cumulTracks[i] && currentTrack > cumulTracks[i - 1])
    {
      fldr = i + 1;
      track = currentTrack - cumulTracks[i - 1];
    }
  }
} // End getRaw
// --------------------
// Convert currentTrack to (fldr, track) Redundant?
void rawToFT()
{
  // Folder 1 code is special
  if (currentTrack <= cumulTracks[0]) // It's in folder 1
  {
    fldr = 1;
    track = currentTrack;
  }
  // Now test other ranges, e.g. for 26 folders test against
  // cumulTracks[] elements [1] to [25] (array indexes start at 0).
  for (int i = 1; i < numFldrs - 1; i++)
  {
    if (currentTrack <= cumulTracks[i] && currentTrack > cumulTracks[i - 1])
    {
      fldr = i + 1;
      track = currentTrack - cumulTracks[i - 1];
    }
  }
}
// --------------------
// Convert randomTrack to (fldr, track) & print
void randToFT()
{
  // For 'special, short' content, e.g msd 'S6'
  // e.g cumulTracks[0] = 20,  cumulTracks[26] = 1651; 18 Feb 2023
  for (int i = 0; i < numFldrs - 1; i++)
  {
    if (randomTrack <= trackCount && randomTrack > cumulTracks[i - 1])
    {
      fldr = i + 1;
      track = randomTrack - cumulTracks[i - 1];
    }
  }
  //  Serial.print(F("mode = "));
  //  Serial.println(mode);
  Serial.print(F("raw randomTrack = "));
  Serial.print(randomTrack);
  Serial.print(F("   fldr = "));
  Serial.print(fldr);
  Serial.print(F("   track = "));
  Serial.println(track);
  Serial.println();
}
// --------------------
void printRandomTrack()
{
  Serial.print(F("Track started = "));
  Serial.print(F("("));
  Serial.print(fldr);
  Serial.print(F(", "));
  Serial.print(track);
  Serial.println(F(")"));
  //  Serial.println();
} // printRandomTrack()
// --------------------
void jumpTenFwd() // Basic method
{
  myDFPlayer.volume(0);  //Set volume value (0~30).
  delay(10);
  for (int i = 1; i < 11; i++)
  {
    myDFPlayer.next();
    delay(250); // 200 50 10 50 100 500, 200, seems sensitive
  }
  myDFPlayer.volume(15);
  delay(50);
}
// --------------------
void jumpTenBack() // Basic method
{
  myDFPlayer.volume(0);  //Set volume value (0~30).
  delay(10);
  for (int i = 1; i < 11; i++)
  {
    myDFPlayer.previous();
    delay(250); // last 200 150 50 10 50 100 400 Was 500, seems sensitive
  }
  myDFPlayer.volume(15);
  delay(50);
}
// --------------------


////////////////////////  CASES  /////////////////////////////////
void actionCases() // Using Toggle library button
// Wed 8 Feb 2023 1154: Add more cases 27-30.
{
  switch (folderCode) // Obviousl depends on mSD content
  {
    case 0xF1: // Want to play all 20 tracks of folder 1 from trackArray[]
      mode = 1; // Not sure these are needed, but leave for now
      Serial.println();
      Serial.println("Case 1 has been selected");
      randNumberF = 1; // 01-1950 UK No 1s
      folderSize = 20;
      i = 0; // Crucial to have this before playTrackArray();
      createTrackArray(); // Same as used successfully in setup()
      myDFPlayer.stop(); // Not sure these are needed?
      // Wednesday 28 December 2022, 1511 test if following is redundant
      //      playTrackArray();  // That works - but strangely so does this
      //      myDFPlayer.playFolder(randNumberF, trackArray[0]);
      delay(btnGrpDelay); // See spreadsheet TEMP
      playTrackArray();
      break;

    case 0xF2:
      mode = 1;
      Serial.println();
      Serial.println("Case 2 has been selected");
      randNumberF = 2; // 02-1950-54 Pop
      folderSize = 4; //
      i = 0;
      createTrackArray();
      myDFPlayer.stop();
      delay(btnGrpDelay);
      playTrackArray();
      break;
    case 0xF3:
      mode = 1;
      Serial.println("Case 3 has been selected");
      randNumberF = 3; // 03-1955-57 Pop
      folderSize = 2;
      i = 0;
      createTrackArray();
      myDFPlayer.stop();
      delay(btnGrpDelay);
      playTrackArray();
      break;
    case 0xF4:
      mode = 1;
      Serial.println("Case 4 has been selected");
      randNumberF = 4; // 04-1958-59 Pop
      folderSize = 4;
      i = 0;
      createTrackArray();
      myDFPlayer.stop();
      delay(btnGrpDelay);
      playTrackArray();
      break;
    case 0xF5:
      mode = 1;
      Serial.println("Case 5 has been selected");
      randNumberF = 5; // 05-1960s Pop A-G
      folderSize = 1;
      i = 0;
      createTrackArray();
      myDFPlayer.stop();
      delay(btnGrpDelay);
      playTrackArray();
      break;
    case 0xF6:
      mode = 1;
      Serial.println("Case 6 has been selected");
      randNumberF = 6; // 06-1960s Pop H-Z
      folderSize = 4;
      i = 0;
      createTrackArray();
      myDFPlayer.stop();
      delay(btnGrpDelay);
      playTrackArray();
      break;
    case 0xF7:
      mode = 1;
      Serial.println("Case 7 has been selected");
      randNumberF = 7; // 07-1970s Pop
      folderSize = 3;
      i = 0;
      createTrackArray();
      myDFPlayer.stop();
      delay(btnGrpDelay);
      playTrackArray();
      break;
    case 0xF8:
      mode = 1;
      Serial.println("Case 8 has been selected");
      randNumberF = 8; // 08-1980s Pop
      folderSize = 4;
      i = 0;
      createTrackArray();
      myDFPlayer.stop();
      delay(btnGrpDelay);
      playTrackArray();
      break;
    case 0xF9:
      mode = 1;
      randNumberF = 9; // Acoustic Alchemy
      folderSize = 37;
      i = 0;
      createTrackArray();
      myDFPlayer.stop();
      delay(btnGrpDelay);
      playTrackArray();
      break;
    case 0x10:
      mode = 1;
      Serial.println();
      randNumberF = 10; // Streisand
      folderSize = 26;
      i = 0;
      createTrackArray();
      myDFPlayer.stop();
      delay(btnGrpDelay);
      playTrackArray();
      break;
    case 0x11:
      mode = 1;
      randNumberF = 11; // Beatles
      folderSize = 78;
      i = 0;
      createTrackArray();
      myDFPlayer.stop();
      delay(btnGrpDelay);
      playTrackArray();
      break;
    case 0x12:
      mode = 1;
      randNumberF = 12; // Big Band
      folderSize = 252;
      i = 0;
      createTrackArray();
      myDFPlayer.stop();
      delay(btnGrpDelay);
      playTrackArray();
      break;
    case 0x13:
      mode = 1;
      randNumberF = 13; // Christmas
      folderSize = 71;
      i = 0;
      createTrackArray();
      myDFPlayer.stop();
      delay(btnGrpDelay);
      playTrackArray();
      break;
    case 0x14:
      mode = 1;
      randNumberF = 14; // Classical
      folderSize = 69;
      i = 0;
      createTrackArray();
      myDFPlayer.stop();
      delay(btnGrpDelay);
      playTrackArray();
      break;
    case 0x15:
      mode = 1;
      randNumberF = 15; // Conniff
      folderSize = 185;
      i = 0;
      createTrackArray();
      myDFPlayer.stop();
      delay(btnGrpDelay);
      playTrackArray();
      break;
    case 0x16:
      mode = 1;
      randNumberF = 16; // Ella
      folderSize = 24;
      i = 0;
      createTrackArray();
      myDFPlayer.stop();
      delay(btnGrpDelay);
      playTrackArray();
      break;
    case 0x17:
      mode = 1;
      randNumberF = 17; // Elvis
      folderSize = 81;
      i = 0;
      createTrackArray();
      myDFPlayer.stop();
      delay(btnGrpDelay);
      playTrackArray();
      break;
    case 0x18:
      mode = 1;
      randNumberF = 18; // FAVOURITES
      folderSize = 162;
      i = 0;
      createTrackArray();
      myDFPlayer.stop();
      delay(btnGrpDelay);
      playTrackArray();
      break;
    case 0x19:
      mode = 1;
      randNumberF = 19; // Sinatra
      folderSize = 185;
      i = 0;
      createTrackArray();
      myDFPlayer.stop();
      delay(btnGrpDelay);
      playTrackArray();
      break;
    case 0x20:
      mode = 1;
      randNumberF = 20; // Jazz
      folderSize = 77;
      i = 0;
      createTrackArray();
      myDFPlayer.stop();
      delay(btnGrpDelay);
      playTrackArray();
      break;
    case 0x21:
      mode = 1;
      randNumberF = 21; // Movies & Musicals
      folderSize = 91;
      i = 0;
      createTrackArray();
      myDFPlayer.stop();
      delay(btnGrpDelay);
      playTrackArray();
      break;
    case 0x22:
      mode = 1;
      randNumberF = 22; // Ray Charles
      folderSize = 33;
      i = 0;
      createTrackArray();
      myDFPlayer.stop();
      delay(btnGrpDelay);
      playTrackArray();
      break;
    case 0x23:
      mode = 1;
      randNumberF = 23; // Simply Red
      folderSize = 37;
      i = 0;
      createTrackArray();
      myDFPlayer.stop();
      delay(btnGrpDelay);
      playTrackArray();
      break;
    case 0x24:
      mode = 1;
      randNumberF = 24; // Swingle
      folderSize = 43;
      i = 0;
      createTrackArray();
      myDFPlayer.stop();
      delay(btnGrpDelay);
      playTrackArray();
      break;
    case 0x25:
      mode = 1;
      randNumberF = 25; // Trad Jazz
      folderSize = 73;
      i = 0;
      createTrackArray();
      myDFPlayer.stop();
      delay(btnGrpDelay);
      playTrackArray();
      break;
    case 0x26:
      mode = 1;
      randNumberF = 26; // TV Themes
      folderSize = 83;
      i = 0;
      createTrackArray();
      myDFPlayer.stop();
      delay(btnGrpDelay);
      playTrackArray();
      break;
    //---------------------------
    case 0x27:
      mode = 1;
      randNumberF = 27;
      folderSize = 3;
      i = 0;
      createTrackArray();
      myDFPlayer.stop();
      delay(btnGrpDelay);
      playTrackArray();
      break;
    case 0x28:
      mode = 1;
      randNumberF = 28;
      folderSize = 3;
      i = 0;
      createTrackArray();
      myDFPlayer.stop();
      delay(btnGrpDelay);
      playTrackArray();
      break;
    case 0x29:
      mode = 1;
      randNumberF = 29;
      folderSize = 3;
      i = 0;
      createTrackArray();
      myDFPlayer.stop();
      delay(btnGrpDelay);
      playTrackArray();
      break;
    case 0x30:
      mode = 1;
      randNumberF = 29;
      folderSize = 3;
      i = 0;
      createTrackArray();
      myDFPlayer.stop();
      delay(btnGrpDelay);
      playTrackArray();
      break;
    default:
      // statements
      break;
  }
}

///////////////////////  DFR FUNCTION  ////////////////////////////
void printDetail(uint8_t type, int value)
{
  switch (type)
  {
    case TimeOut:
      Serial.println(F("Time Out!"));
      break;
    case WrongStack:
      Serial.println(F("Stack Wrong!"));
      break;
    case DFPlayerCardInserted:
      Serial.println(F("Card Inserted!"));
      break;
    case DFPlayerCardRemoved:
      Serial.println(F("Card Removed!"));
      break;
    case DFPlayerCardOnline:
      Serial.println(F("Card Online!"));
      break;
    case DFPlayerUSBInserted:
      Serial.println(F("USB Inserted!"));
      break;
    case DFPlayerUSBRemoved:
      Serial.println(F("USB Removed!"));
      break;
    case DFPlayerPlayFinished:
      //      Serial.println(F(" Play Finished!"));
      break;

    case DFPlayerError:
      Serial.print(F("DFPlayerError : "));
      switch (value)
      {
        case Busy:
          Serial.println(F("Card not found"));
          break;
        case Sleeping:
          Serial.println(F("Sleeping"));
          break;
        case SerialWrongStack:
          Serial.println(F("Get Wrong Stack"));
          break;
        case CheckSumNotMatch:
          Serial.println(F("Check Sum Not Match"));
          break;
        case FileIndexOut:
          Serial.println(F("File Index Out of Bound"));
          break;
        case FileMismatch:
          Serial.println(F("Cannot Find File"));
          break;
        case Advertise:
          Serial.println(F("In Advertise"));
          break;
        default:
          break;
      }
      break;
    default:
      break;
  }
}

I know you are looking at data not program memory, but there are 28 occurrences of

  createTrackArray();
  myDFPlayer.stop();
  delay(btnGrpDelay);
  playTrackArray();

which might take less memory than 28 calls to a function that did whatever that does.

And be easier to change if it turned out you needed to.

Just sayin'.

a7

Thanks, I’ll see what saving I get after moving those into a function.

But the outlook isn’t good, given that the current special test code is for a much reduced track content.

There are several places where you did not use the F() macro when printing text literals, that amounts to a couple of hundred bytes of ram.

The arrays cumulTracks and folderSizes are const, they can be stored in PROGMEM.

Mostly debugging. I ‘F’ them at intervals.

Sounds promising, thanks. New area for me, so will have to study,

a)
follow that advise:

search your code for

println("

and

print("

and you will find them fast

b)
for single characters just print one char

Serial.println(")");  // will cost you two bytes
Serial.println(')');  // single quoted is a char, just one byte

c)
Not so much to save SRAM, but may be program space, get rid off your duplicated code sequences

      mode = 1;
      randNumberF = 21; // Movies & Musicals
      folderSize = 91;
      i = 0;
      createTrackArray();
      myDFPlayer.stop();
      delay(btnGrpDelay);
      playTrackArray();

d)

the first cases print such a line:

Serial.println("Case 1 has been selected");

all these line can be replaced with an if

if (folderCode <= 0xF8) {
Serial.print(F("Case "));
Serial.print(folderCode & 0x0F);
Serial.println(F(" has been selected");
}

e)
is randNumberF = 29 in case 0x30 correct?
if it should be 30 ... I see a direct connection on folderCode, consider to replace randNumberF.

Thanks.

Re the quotes, is it therfore simply better to change all doubles “ to singles ‘ ?

Re the duplicated code what’s the best way to fix that? A function, as in my reply to alto777 who made the same suggestion?

But more generally, I reckon I’m looking for much larger economies!

yes alto777 spotted it similar.
But don't stop with the functions. Make an structure array with the differing values mode/randNumberF ???/folderSize and just hand over to a function a variable with the folderCode and let the function select the values.
So a complete replacement / rewrite of the function actionCases() ... this switch case makes nearly no sense with all that code duplicates.

1 Like

NO.

Not sure I follow that noisca, but I'll study it later. Also not sure you saw that folderCode is a function of the Toggle library? Which gets its value from the number of 'short' and 'long' presses of a single button? My 26 Cases then determine the action taken: the main one being to play that chosen folder's tracks (randomly and without repetition) until othewise stopped.

Its your code, but I'm very confident ... what you use in the switch case is a global variable defined and declared by you:

byte folderCode; // For Toggle library

OK, then I haven't properly understood. I'll try it myself later but do you mean this tip only works for a single character?

yes it will work only for one character.

Because

')'

is character which can be stored in an one byte variable.

")"
is a string literal and needs a Nullterminator so it costs two bytes. the ) and the Nullterminator.

More generically, consider all declared variables. If it is intended that the item never change, it likely could be "const", which will change it's allocation.

Thanks, understood.

Thanks. At a quick scan I could find only one, now reallocated:
const byte numFldrs = 26;


Also more generically, coming back to my original query:
"I think they arise whenever my global variables get over 75% of dynamic memory. That seems a surprisingly low bar?"

I don't start fretting seriously about my C: drive until maybe 90%. What's the consensus on this? Are many here using 85/90%/95% of dynamic memory. Are "stability problems" occurring, and if so what form do they take?