Моя программа использует слишком много памяти

Я работаю на складе, и мне было поручено создать устройство, которое будет показывать местоположение любого продукта, когда будет указан идентификатор продукта. Весь наш продукт состоит из 3 вещей. Идентификатор продукта, номер стойки и номер зазора.

Таким образом, базовая программа выглядит так, когда я ввожу идентификатор продукта: 1640 Программа покажет на дисплее, что она находится в RCK 113 GAP 88

Для этого я использовал Arduino Mega, клавиатуру 4x3 и ЖК-дисплей 16x2 с адаптером I2C. Я нашел код калькулятора клавиатуры Arduino в Интернете и изменил свой код оттуда. Так что в настоящее время мой код работает нормально, и все

Что я заметил, так это то, что у Arduino mega осталось всего 2% памяти со всеми этими строковыми значениями (для этого я сначала не мог использовать Arduino Uno). Теперь из-за добавления новых продуктов мне нужно больше места или способов хранения данных в другом месте.

Я не могу использовать интернет или WiFi, а другая команда уже сделал устройство с помощью с ESP8266 Nodemcu, который показывает данные из Google в лист, но поскольку мое устройство хранит данные в себе это очень быстро (практически мгновенно), чтобы показать какой-то результат, с другой стороны, устройства с ESP8266 NODEMCU может быть легко обновлены и не заморачиваться по поводу размера, но это очень медленно, чтобы показать результат и хорошее соединение WiFi так не эффективно в складских условиях. Поэтому каждый сотрудник использует мое устройство на ходу и другое устройство в качестве резервной копии

В настоящее время у меня на устройстве хранится около 1000 адресов продуктов, но скоро поступит новая партия почти 3000 продуктов, и у моего mega осталось всего 2% памяти.

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

Мой код приведен ниже. Это огромно, пожалуйста, потратьте время, чтобы понять это.

Даже я не могу скопировать свой код здесь

Текст ограничен 30000 символами; вы ввели 132142.

Это пример моего кода, который вы должны понять

Вот полный загруженный код. Скачать

Я хочу знать, можно ли добавить модуль SD-карты Arduino и сохранить там часть моей строковой памяти.

#include <LiquidCrystal_I2C.h>
#include <Keypad.h> 

const byte ROWS = 4; 
const byte COLS = 4;
unsigned int integerValue=0; 
//define the keymap
char keys [ROWS] [COLS] = {
  {'1', '2', '3', 'X'},
  {'4', '5', '6', '*'},
  {'7', '8', '9', '='},
  {'/', '0', '-', '+'}
};
byte rowPins[ROWS] = {9, 8, 7, 6}; 
byte colPins[COLS] = {5, 4, 3, 2};

//create the keypad
Keypad myKeypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
LiquidCrystal_I2C lcd(0x27, 16, 2);

//variables declaration
boolean valOnePresent = false;
boolean result = false;
String num1;
int ans;
boolean getvalue = false;


void setup(){
  Serial.begin(9600);
  lcd.init(); // initialize the lcd
  lcd.backlight();
  frstscreen();
  //lcd.clear();

}

void loop(){
  char key = myKeypad.getKey();

  if (key != NO_KEY && (key=='1'||key=='2'||key=='3'||key=='4'||key=='5'||key=='6'||key=='7'||key=='8'||key=='9'||key=='0')){
      num1 = num1 + key;
      int numLength = num1.length();
      lcd.setCursor(15 - numLength, 1);
      lcd.print(num1);
      valOnePresent = true;
  }

  else if (valOnePresent == true && key != NO_KEY && key == '='){

      ans = num1.toInt();
      
      lcd.clear();
      lcd.setCursor(15,1);
      lcd.autoscroll();
      lcd.print(ans);
      lcd.noAutoscroll();
      result = true;
      
  }
  else if (key != NO_KEY && key == 'X'){
    lcd.clear();
    valOnePresent = false;
    num1 = "";
    frstscreen();
    getvalue = false;
}

    integerValue = ans;


if (result == true) {

if (integerValue ==  9   ) { IDScrn(); lcd.print("109");lcd.setCursor(12,1);lcd.print("63 "); delayfunc();}
if (integerValue == 15    ) { IDScrn(); lcd.print("107");lcd.setCursor(12,1);lcd.print("60 "); delayfunc();}
if (integerValue == 15    ) { IDScrn(); lcd.print("109");lcd.setCursor(12,1);lcd.print("63 "); delayfunc();}
if (integerValue == 20    ) { IDScrn(); lcd.print("109");lcd.setCursor(12,1);lcd.print("63 "); delayfunc();}
if (integerValue == 22    ) { IDScrn(); lcd.print("110");lcd.setCursor(12,1);lcd.print("68 "); delayfunc();}
if (integerValue == 23    ) { IDScrn(); lcd.print("109");lcd.setCursor(12,1);lcd.print("63 "); delayfunc();}

//TOP and components

if (integerValue ==  1001   ) { IDScrn(); lcd.print("RCK 115 COL A                  "); delayfunc();}
if (integerValue == 1002   ) { IDScrn(); lcd.print("RCK 115 COL A                  "); delayfunc();}
if (integerValue == 1003   ) { IDScrn(); lcd.print("RCK 115 COL A                  "); delayfunc();}


//Stock and CBOX

if (integerValue ==   376    ) { ComScrn(); lcd.print(" Stock Box 1"); delayfunc();}
if (integerValue ==   15     ) { ComScrn(); lcd.print(" Stock Box 1"); delayfunc();}
if (integerValue ==   919    ) { ComScrn(); lcd.print(" Stock Box 1"); delayfunc();}



//CBOX

if (integerValue ==   245    ) { ComScrn(); lcd.print(" 39K COL I "); delayfunc();}
if (integerValue ==   245    ) { ComScrn(); lcd.print(" 10K COL I "); delayfunc();}
if (integerValue ==   245    ) { ComScrn(); lcd.print(" 47k COL I "); delayfunc();}
if (integerValue ==   245    ) { ComScrn(); lcd.print(" 1k  COL I "); delayfunc();}
if (integerValue ==   245    ) { ComScrn(); lcd.print(" 2k  COL I "); delayfunc();}
if (integerValue ==   245    ) { ComScrn(); lcd.print(" 4.7k  COL I "); delayfunc();}
if (integerValue ==   245    ) { ComScrn(); lcd.print(" 27k COL I "); delayfunc();}
if (integerValue ==   766    ) { ComScrn(); lcd.print(" 0 ohm   COL I "); delayfunc();}
if (integerValue ==   766    ) { ComScrn(); lcd.print(" 220 ohm COL I "); delayfunc();}
if (integerValue ==   766    ) { ComScrn(); lcd.print(" 560 ohm COL I "); delayfunc();}
if (integerValue ==   766    ) { ComScrn(); lcd.print(" 470 ohm COL I "); delayfunc();}
if (integerValue ==   766    ) { ComScrn(); lcd.print(" 330 ohm COL I "); delayfunc();}
if (integerValue ==   766    ) { ComScrn(); lcd.print(" 100 ohm COL I "); delayfunc();}
if (integerValue ==   1291     ) { ComScrn(); lcd.print(" Cbox 1  GAP 101 "); delayfunc();}
if (integerValue ==   1314     ) { ComScrn(); lcd.print(" Cbox 1  GAP 101 "); delayfunc();}

if (getvalue == false) { noproduct(); }

} 
 }
void frstscreen()
{
  lcd1stline();
  lcd.print("Warehouse Stock");
  lcd.setCursor(0,1);
  lcd.print("Enter ID: ");
}
void pressA(){

  lcd.clear();
  lcd.setCursor(0,1);
  lcd.print("Press A again");
}
void noproduct(){

  lcd1stline();
  lcd.print("Not Found");
  lcd.setCursor(0,1);
  lcd.print("Press A again");
  result= false;
}
void delayfunc(){
  result = false;
  delay (2000);
  pressA();
  getvalue = true;

  
}
void lcd1stline(){
lcd.clear();
lcd.setCursor(0,0);
}
void IDScrn(){
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("ID: ");
  lcd.setCursor(4,0);
  lcd.print(integerValue);
  lcd.setCursor(0,1);
  lcd.print("RCK     GAP");
  lcd.setCursor(4,1);
}

void ComScrn(){
    lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("ID: ");
  lcd.setCursor(4,0);
  lcd.print(integerValue);
  lcd.setCursor(1,1);
}

, 👍2

Обсуждение

Вы думали о хранении данных на SD-карте? Быстро, без необходимости в Интернете, *и* легко обновляется. И никаких проблем с памятью., @Majenko

Использование SD-карты в качестве памяти для создания вашей базы данных-это правильный путь, потому что это доказательство будущего. По мере увеличения количества товаров на складе в какой-то момент вы можете достичь предела памяти Mega. С помощью SD - карты вы никогда не достигнете этого с помощью простой текстовой базы данных элементов. Кроме того, это заставит вас написать ОДНУ функцию, чтобы получить ЛЮБУЮ запись из базы данных. Каждый раз, когда вы видите, что пишете в основном одни и те же строки кода много раз, вам действительно следует подумать о лучшей структуре кода, @chrisl

вопрос в том, "Что я хочу знать, так это то, можно ли добавить модуль SD-карты Arduino и сохранить там часть моей строковой памяти"., @Juraj

Склад... коммерческие... операции... не используйте Arduino. Серьезно. Это игрушка. Существуют гораздо более подходящие контроллеры SBC и микроконтроллеры с гораздо большим количеством ресурсов для решения масштабируемых реальных проблем. Вы пытаетесь решить бизнес - проблему. Экономия оперативной памяти-это НЕ та проблема, с которой вам нужно обременять себя, когда у вас есть реальная работа, которую нужно выполнить. Сейчас уже не время для хобби - от этого зависит бизнес. Не сковывайте себя слабыми и неподходящими инструментами., @J...

@Дж... Я не уверен, что ардуино не следует использовать для коммерческих операций, особенно в качестве прототипа (что, безусловно, так и есть)., @user253751

вы также можете рассмотреть возможность обновления до Raspberry Pi, который имеет больше гигабайт памяти, чем вам когда-либо понадобится для этой программы, но также потребляет больше энергии, и вам приходится иметь дело с Linux, @user253751

@user253751 Создание прототипа на платформе, которую вам нужно будет выбросить и начать заново, когда вы поймете, что проблемы с инвентаризацией бизнеса, связанные с базой данных, не помещаются в 8 КБ оперативной памяти, абсолютно бессмысленно. Сейчас уже не 1980 год. Нам не нужно специально усложнять нашу жизнь, используя абсурдно дорогое и недостаточно мощное оборудование. Arduino даже не дешев - это просто грубый недостаток, без каких-либо оправданий для его выбора. Вы ничего не будете делать, кроме как бороться за то, чтобы втиснуть решение в этот крошечный чип и оставить себе нулевое пространство для роста и расширения. Почему?, @J...

@user253751I считает, что Raspberry Pi будет гораздо лучшей платформой, если это "устройство" должно быть компактным. Намного проще обновлять, может содержать всю базу данных в памяти, и приложение может быть проще разрабатывать для не встроенных разработчиков, @selectstriker2

Ваши повторяющиеся комментарии звучат так, как будто у вас проблема _X для Y_, и вы не понимаете, что люди указывают на то, что реальное решение представленной проблемы-это Y., @JDługosz


4 ответа


1

Вы пробовали использовать макрос (PROGMEM) F? Это описано в официальной документации Arduino в конце https://www.arduino.cc/reference/en/language/variables/utilities/progmem/

Это заставляет все строки храниться во флэш - памяти вместо дефицитной памяти SRAM.

Вы можете попробовать с помощью нескольких строк, чтобы увидеть, уменьшается ли объем памяти, и рассчитать, будет ли этого достаточно (чтобы поместиться во флэш-память). Если даже флэш недостаточно, то альтернативой будет SD - карта.

Также некоторые рефакторинги, которые могут очистить код (не обязательно уменьшить объем используемой памяти):

  • вместо множества конструкций if (integerValue == xxx) используйте операторы switch/case.
  • Возможной ошибкой являются одинаковые значения внутри (например, 245 и 766, используемые много раз, но для разных продуктов).
  • Переместите функцию lcd.print("...") внутрь функции ComScrn и передайте строку (используя F(..))
  • Аналогично приведенному выше, но для IDScrn, используйте две строки и также установите курсор внутри IDScrn.
  • (ключ != NO_KEY && ключ == 'X') ... если ключ "X", он никогда не может быть NO_KEY, поэтому первая проверка может быть удалена.
  • if (результат == true) можно записать как if (результат).
,

вопрос в том, "Что я хочу знать, так это то, можно ли добавить модуль SD-карты Arduino и сохранить там часть моей строковой памяти"., @Juraj

@Juraj Просто: да (но моя альтернатива, возможно, делает это ненужным., @Michel Keijzers


9

В вашем коде много повторений. Этого , как правило, следует избегать не только из-за использования памяти, но и, что наиболее важно, из-за ремонтопригодности.

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

// Структура данных, описывающая записанное местоположение.
struct Location {
    uint16_t id;
    uint8_t rack;
    uint8_t gap;
};

// Список зарегистрированных местоположений продуктов.
const Location locations[] = {
    {  9, 109, 63 },
    { 15, 109, 63 },
    { 20, 109, 63 },
    { 22, 110, 68 },
    { 23, 109, 63 },
    // etc...
};

// Количество пунктов в приведенном выше списке.
const size_t nb_locations = sizeof locations / sizeof locations[0];

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

Затем весь код, обрабатывающий эти данные, становится циклом, а не повторением. Например, следующая функция может печатать местоположение любого элемента на ЖК-дисплее. Он возвращает значение true или false, чтобы сообщить вам , нашел ли он запрошенный элемент:

// Если элемент найден, выведите его местоположение и верните значение true.
// В противном случае верните значение false.
bool print_location(uint16_t id) {
    for (size_t i = 0; i < nb_locations; i++) {
        if (locations[i].id == id) {
            IDScrn();
            lcd.print(locations[i].rack);
            lcd.setCursor(12,1);
            lcd.print(locations[i].gap);
            delayfunc();
            return true;  // найдено
        }
    }
    return false;  // не найдено
}

В этой схеме по-прежнему существует проблема, связанная с тем, что все данные копируются в оперативную память при инициализации. Если у вас есть 4000 предметов, для этого потребуется 16 000 байт оперативной памяти, или примерно в два раза больше оперативной памяти вашего Мега. Вы можете сэкономить оперативную память, сохранив все постоянные данные во флэш-памяти, используя программу квалификатор. У вас в 32 раза больше флэш-памяти, чем оперативной памяти. Однако затем вам потребуется вернуть каждый элемент в оперативную память, чтобы использовать его, что можно сделать с помощью функции memcpy_P(). Вот версия предыдущего кода, который хранит массив во флэш-памяти:

// Список зарегистрированных местоположений продуктов.
PROGMEM const Location locations[] = {
    {  9, 109, 63 },
    { 15, 109, 63 },
    { 20, 109, 63 },
    { 22, 110, 68 },
    { 23, 109, 63 },
    // etc...
};

// Количество пунктов в приведенном выше списке.
const size_t nb_locations = sizeof locations / sizeof locations[0];

// Если элемент найден, выведите его местоположение и верните значение true.
// В противном случае верните значение false.
bool print_location(uint16_t id) {
    for (size_t i = 0; i < nb_locations; i++) {
        Location location;  // Копия местоположения в оперативной locations[i]
        memcpy_P(&location, &locations[i], sizeof location);
        if (location.id == id) {
            IDScrn();
            lcd.print(location.rack);
            lcd.setCursor(12,1);
            lcd.print(location.gap);
            delayfunc();
            return true;  // найдено
        }
    }
    return false;  // не найдено
}

Такой подход должен позволить вам хранить всю базу данных во флэш -памяти вашего Mega и, возможно, даже в Uno. Тем не менее, я поддерживаю комментаторов, которые говорят, что использование SD-карты должно сделать обновление базы данных намного более удобным.

,

Я не знал о memcpy_p ... голосовали за, @Michel Keijzers


0

Я хочу знать, можно ли добавить модуль SD-карты Arduino и сохранить там часть моей строковой памяти

Ну, учитывая, что два участника уже дали несколько советов как по улучшению вашего кода, так и по преимуществам использования SD-карты. Мне больше нечего сказать, кроме как ответить на ваш очень конкретный вопрос.

Ответ прост, и это "да". Нет никаких проблем в разделении ваших данных на две части, одну часть на вашем микроконтроллере/Arduino, а другую часть на вашей SD-карте, вам нужно будет знать, какая часть находится там, чтобы вы могли получить к ней доступ без каких-либо проблем и почти мгновенно.

Я надеюсь, что это полностью ответит на ваш вопрос.

,

Чтобы поместить _data_ на SD-карту, оператору все равно сначала придется написать программу, чтобы _data_ был в структуре данных, а не повторял операторы if/print. Так что другие ответы все еще применимы., @JDługosz

Действительно, так оно и есть, вот почему я очень четко заявил в начале своего ответа, что другие ответы уже дали несколько советов о том, как улучшить код, в которых, несомненно, упоминалось использование структур данных, однако он продолжал комментировать эти ответы и снова задавал тот же вопрос. Я хочу знать, можно ли добавить модуль SD-карты Arduino и сохранить там часть моей строковой памяти Так что в принципе, да., @Abd-AlRahman Muhammad


-1

Arduino Uno использует довольно старый, слабый процессор. Это по - прежнему совершенно новый микроконтроллер - он работает просто отлично, - но существуют гораздо более новые.

Вы упомянули ESP8266. Знаете ли вы, что у него 40 КБ оперативной памяти по сравнению с 2 КБ Uno? А преемник, ESP32, имеет 320 Кб оперативной памяти.

Поскольку ваше приложение в основном представляет собой миниатюрный компьютер с клавиатурой и экраном, рассматривали ли вы возможность использования чего-то, разработанного как миниатюрный компьютер? У Raspberry Pi (любой модели) больше гигабайт оперативной памяти, чем вы знаете, что с ней делать. Но он использует гораздо больше энергии. Но вы также можете написать программное обеспечение обычным способом Linux (если вам нравится программирование на Linux). Или вы могли бы подумать о написании приложения для вашего мобильного телефона Android.

Если вы настаиваете на использовании Uno, другой вариант-добавить чип расширения оперативной памяти SPI, например, этот чип за 1 евро с 32 Кб (они становятся больше). Теперь Uno не имеет встроенной возможности расширения оперативной памяти, поэтому вы не можете просто получить к ней доступ с помощью указателей или чего-то в этом роде, но вы все равно можете использовать шину SPI, чтобы указать чипу считывать или записывать определенные байты, как это можно сделать с помощью SD-карты.

После написания этого я понял, что оперативная память SPI не так полезна для вашего приложения, потому что вы не хотите хранить данные только при включенном устройстве. Вместо этого вы можете использовать микросхему SPI FRAM или SPI EEPROM, которая может хранить данные при выключенном питании. В среде IDE Arduino нет способа автоматического ввода данных ни в один из этих чипов... поэтому, чтобы ввести данные, вам, вероятно, придется отправить их через последовательный порт в отдельную программу, которую вы написали, которая записывает их на чип. Или вы можете создать пользовательский интерфейс, позволяющий пользователю редактировать данные непосредственно на устройстве.

Или вы можете использовать SD-карту, да. Честно говоря, это, наверное, самый простой способ.

,