Запрос на помощь в сжатии кода и сохранении памяти

Я строю окончательный проект для класса, который включает в себя воспроизведение аудиофайла волнового щита каждый раз, когда я сканирую amiibo с помощью RFID-сканера. Я довольно неопытный программист, поэтому я не очень хорошо знаком с тем, как лучше всего уменьшить использование DRAM. Таким образом, при попытке выполнить последнюю итерацию кода я получил следующую ошибку:

Глобальные переменные используют 2801 байт (136%) динамической памяти, оставляя -753 байта для локальных переменных. Максимум-2048 байт.

Вот мой код:

////// 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 /////////////////

Мое первое предположение о том, что может занимать больше всего памяти, - это оператор switch. Когда я раскомментирую его, использование памяти подскочит с 90% до более чем 130%. Я пробовал другие способы заставить этот код работать, например конкатенацию текста, но это единственный способ, который, казалось, работал надежно, пока у меня не закончилась память.

Я также использую множество библиотек, чтобы помочь использовать щиты и ЖК-дисплей, которые я прикрепил к Uno. Я не мог избавиться от этих библиотек, не поставив под угрозу проект.

Кроме этого, я не знаю, что делать. Если у кого-то есть какие-либо предложения по сжатию кода, я был бы очень признателен за любой совет, который вы можете предложить.

Спасибо.

, 👍3

Обсуждение

Честно говоря, я не вижу большого потенциала для оптимизации, не касаясь библиотек. Комментируя случай переключения, вы получаете столько памяти, потому что компилятор затем оптимизирует все материалы для воспроизведения wav, которые используются только в операторе переключения. Может быть, вам стоит переключиться на плату с большим количеством оперативной памяти, @chrisl

Я думаю, что вы можете поместить все строковые литералы в пространство программы с помощью помощника по строкам, но я не думаю, что этого будет достаточно, так как вы хотите достичь, возможно, 75% (компилятор, я полагаю, не учитывает все локальные переменные)., @chrisl

Использование памяти здесь явно происходит из библиотек, которые вы используете. Комбинация библиотеки считывателей SD - карт + ЖК-дисплей + считыватель NFC кажется слишком большой для Arduino Uno. Я не могу создать ваш код, так как не могу найти библиотеки, которые вы используете. Установлены ли эти стандартные библиотеки через менеджера библиотек?, @PMF

придира: у Arduino Uno есть ATmega328, который имеет [SRAM](https://en.wikipedia.org/wiki/Static_random-access_memory), а не [ДРАМ](https://en.wikipedia.org/wiki/Dynamic_random-access_memory). Фраза "динамическая память", по-видимому, означает просто динамическую в более общем смысле, то есть энергонезависимую память, используемую во время выполнения, в отличие от энергонезависимой программной памяти, которая в основном используется для записи. во время выполнения. С другой стороны, DRAM-это особый способ реализации (энергозависимой) оперативной памяти., @ilkkachu


3 ответа


1

Просто попробуйте переключиться с UNO на MEGA код должен работать после того, как замедления исправлены. Компилятор не говорит вам, сколько памяти на самом деле используется. У вас есть куча и стек и т. Д.

,

Я подумывал о том, чтобы получить Мега; это кажется стоящим. К сожалению, этот проект должен быть завершен через несколько дней. Я могу схватить его, хотя бы для того, чтобы удовлетворить собственное любопытство. Спасибо за предложение., @Ethan Braun

Обычно я начинаю с более крупного процессора, такого как Mega, а когда закончу, могу поместить его в меньший блок. Это дает мне место для отладочных инструкций печати и т. Д., @Gil

Итак, я позаимствовал Мега у своего профессора и опробовал его вчера. Как оказалось, волновой щит, который я использую для чтения аудиофайлов и воспроизведения на динамике, явно указывает, что Mega-это версия Arduino, которая по какой-то причине НЕ поддерживается. Это действительно отстой; я хотел значительно расширить рамки этого проекта, но что вы можете сделать?, @Ethan Braun


11

У вас есть огромный переключатель...случай здесь, чтобы решить имя файла, который должен быть воспроизведен. Это стоит много пространства кода, но также и пространства данных, потому что каждая отдельная литеральная строка в этой части кода копируется в оперативную память во время инициализации.

Нет необходимости тратить все эти затраты, так как имена файлов были назначены очень регулярно, так что вы можете построить имя файла алгоритмически. Как правило, всякий раз, когда вы обнаруживаете , что программируете, копируя, вставляя, а затем применяя некоторые предсказуемые модификации к копиям, вы почти наверняка делаете что-то неправильно. Создание повторяющихся копий одного и того же с предсказуемыми изменениями-это то, для чего лучше всего подходят компьютеры. Поэтому вы должны спросить себя, как вы можете поручить компьютеру (в данном случае Arduino) сделать это за вас. Не делайте работу компьютера сами!

В этом случае имя файла строится путем склеивания трех частей вместе:

  • префикс, который зависит от amiiboNo
  • двузначное число между "01" и "03", в зависимости от клипНо
  • постоянный суффикс ".WAV"

Префикс не может быть вычислен алгоритмически, поэтому вам все равно придется записать все возможные префиксы. Тебе бы не помешал большой switch...case для этого, но гораздо удобнее выбрать правильный префикс из массива, индексированного amiiboNo:

const char *file_prefixes[] = {
    "MARIO", "MK",    "INK",    "DK",    "LUCAS", "DUCKH",  "OOT",
    "BOTWL", "8BITL", "TOONL",  "GANDW", "FALCO", "DEDEDE", "MEWTWO",
    "CAPF",  "ROB",   "PACMAN", "CLOUD", "NESS",  "RYU"
};

Затем вы объявляете массив символов char для хранения имени файла и создаете это имя файла, собирая вместе три части, перечисленные выше. Затем весь ваш корпус switch...может быть заменен следующим образом:

char filename[13];           // longest file name: 12 characters + NUL
char file_number[3] = "01";  // 2 digits + NUL
file_number[1] += clipNo;    // file_number: "01", "02" or "O3"
strcpy(filename, file_prefixes[amiiboNo]);
strcat(filename, file_number);
strcat(filename, ".WAV");
playComplete(filename);

Это уже сэкономит вам много оперативной памяти, но, вероятно, недостаточно. Я не видел очевидного неэффективного использования переменных в вашей программе (но я не смотрел слишком подробно), поэтому мы продолжим нацеливаться на литеральные строки.

Проблема с литеральными строками заключается в том, что они копируются из flash в RAM во время инициализации, и, хотя у вас, вероятно, достаточно flash, оперативная память в дефиците. Это проблема, характерная для гарвардских архитектур, поскольку они имеют отдельные адресные пространства для кода и данных. Мы можем использовать некоторые AVR-специфические трюки, чтобы проинструктировать компилятор не выполнять это копирование. Это затрудняет доступ к данным, потому что C++ изначально не знает об адресных пространствах. К счастью, есть некоторые макросы и вспомогательные функции, которые могут сделать это легко в конкретных случаях.

Лучшим помощником в Arduino world является макрос F (). Используйте его каждый раз, когда вы печатаете() или println() литеральную строку, как в:

Serial.println(F("Waiting for an amiibo ..."));

Далее мы должны избавиться от очень длинного списка числовых идентификаторов. Для этого мы используем макрос PSTR() и функцию strcmp_P (), предоставляемые avr-libc. Замените каждую строку, которая выглядит так

if (strcmp(charUID, "479119425560128"))

чем-то вроде

if (strcmp_P(charUID, PSTR("479119425560128")) == 0)

Обратите внимание , что значение, возвращаемое strcmp_P (), должно сравниваться с 0, так как это то, что эта функция, как и strcmp (), возвращает в случае равенства. Спасибо timemage за то, что заметил эту проблему.

Наконец, мы могли бы также сохранить все префиксы файлов во flash, но это было бы сложнее. А пока я предлагаю вам применить описанные здесь приемы и посмотреть, достаточно ли их, чтобы ваша программа подошла.

Правка: подробнее об этом несколько неясном утверждении: file_number[1] += clipNo;

В языке C++ символ-это 8-битное число. В зависимости от платформы он может быть как подписанным (диапазон: -128...+127), так и беззнаковым (0...255). Это название происходит от того факта, что оно чаще всего используется для хранения числового значения символа ASCII. Например, числовое значение 33 означает"!", а 48 -"!". "0", а 65 означает "А".

Определение

char file_number[3] = "01";

создает массив из 3 ячеек, инициализированный следующим содержимым:

             ┌─────┬─────┬─────┐
file_number: │  48 │  49 │   0 │
             └─────┴─────┴─────┘

Используя языковую нотацию для символов, содержание также может быть записано следующим образом:

             ┌─────┬─────┬─────┐
file_number: │ '0' │ '1' │ '\0'│
             └─────┴─────┴─────┘

Последняя ячейка с символом ASCII NUL предназначена для информирования библиотечных функций , таких как strcat (), о том, что именно здесь заканчивается строка символов.

Заявление

file_number[1] += clipNo;

обращается ко второму элементу массива (индексация массива начинается с 0) и увеличивает его числовое значение на величину clipNo. Например, если clipNo равно 1, то значение в средней ячейке будет изменено с 49 (то есть "1") на 50 (то есть "2"). Это зависит от того факта, что Цифры ASCII имеют последовательные числовые значения, что также можно было бы ожидать от любой нормальной кодировки символов.

,

Я знаю, что это возникло в коде операционной системы, и вы, я знаю, что *вы* знаете это, но strcmp(_P) возвращает 0 для "равного"., @timemage

@timemage: Упс! Ты прав. Я знаю, но я не обратил внимания. Я добавил это к ответу, просто чтобы сделать его более заметным., @Edgar Bonet

Как только у меня появится возможность, я внедрю эти исправления и сообщу вам о ходе работы. Спасибо вам за помощь!, @Ethan Braun


4

В качестве незначительного дополнения к превосходному ответу Эдгара Бонета вы могли бы еще больше упростить свой код и сохранить еще больше памяти, переименовав свои файлы.

В частности, вместо того, чтобы хранить список имен символов, индексированных номером Amiibo, и использовать их в качестве префикса имени файла, почему бы не использовать номера Amiibo напрямую? То есть вместо именования файлов

MARIO01.WAV, MARIO02.WAV, MARIO03.WAV, MK01.WAV, MK02.WAV, MK02.WAVи т. Д.

вы могли бы просто назвать их например

AMI01_01.WAV, AMI01_02.WAV, AMI01_03.WAV, AMI02_01.WAV, AMI02_02.WAV, AMI02_03.WAVи т. Д.

и постройте имя в своем коде, например, так:

char filename[13];
int len = snprintf(filename, sizeof(filename), "AMI%02d_%02d.WAV", amiiboNo + 1, clipNo + 1);
if (len < 0 || len >= sizeof(filename)) {
  // TODO: handle error or insufficient buffer size here
}

Главное преимущество здесь заключается в том, что вы сэкономите память, избавившись от необходимости хранить список имен символов. Компромисс, конечно, заключается в том, что ваши имена файлов теперь будут намного менее описательными.

Вероятно, вы могли бы оптимизировать это немного больше, манипулируя байтами имени файла непосредственно, как это делает Эдгар. Но лично я бы предложил начать с универсального решения на основе snprintf и переключиться на прямую манипуляцию байтами только в том случае, если это все еще слишком много. По крайней мере, я бы предположил, что реализация snprintf в стандартной библиотеке, вероятно, будет довольно хорошо оптимизирована для низкого следа. Конечно, я могу и ошибаться.

,

"snprintf ()" - хорошее предложение. Скорее всего, это будет стоить дороже, чем "strcpy ()" и "strcat ()" вместе взятые, потому что, даже если вы используете только "%02d", функция поддерживает широкий спектр спецификаторов преобразования. Я действительно думаю, что это хороший выбор, если у вас достаточно флэш-памяти (скорее всего, в данном случае), потому что это упрощает код. Однако его интерфейс может быть не очень удобным для новичков. ОТО, мое утверждение " file_number[1] += clipNo;", вероятно, также не подходит для новичков. :-/, @Edgar Bonet

@EdgarBonet, как новичок, я могу засвидетельствовать, что меня немного сбило с толку утверждение " номер файла[1] += clipNo;", ха-ха. Если у вас есть время, не могли бы вы объяснить, что вы там делали?, @Ethan Braun

Обновление: Это работает! После изменения строковых литералов и имен файлов с помощью ваших предложений и предложений @EdgarBonet моя программа занимает 84% памяти. Я, вероятно, еще немного повозлюсь с ним, чтобы узнать некоторые дополнительные функции. Спасибо вам обоим!, @Ethan Braun