Ошибка при загрузке скетча // Недостаточно памяти, программа может работать нестабильно.

wizaller
Offline
Зарегистрирован: 07.05.2018

Помогите пожалуйста решить проблему. При загрузке скетча пишет:

Скетч использует 10894 байт (75%) памяти устройства. Всего доступно 14336 байт.Глобальные переменные используют 1000 байт (97%) динамической памяти, оставляя 24 байт для локальных переменных. Максимум: 1024 байт.

Недостаточно памяти, программа может работать нестабильно.
Произошла ошибка при загрузке скетча.
 
Вот текст скетча (скетч написан не мной):
/*
  Модифицировано под дисплей 1602 klykov.net vk.com/ms262 instagram.com/klykovnet
 
  Блок электроники для крутого моддинга вашего ПК, возможности:
  - Вывод основных параметров железа на внешний LCD дисплей
  - Температура: CPU, GPU, материнская плата, самый горячий HDD
  - Уровень загрузки: CPU, GPU, RAM, видеопамять
  - Графики изменения вышеперечисленных параметров по времени
  
  Программа HardwareMonitorPlus  https://github.com/AlexGyver/PCdisplay
  - Запустить OpenHardwareMonitor.exe
  - Options/Serial/Run - запуск соединения с Ардуиной
  - Options/Serial/Config - настройка параметров работы
    - PORT address - адрес порта, куда подключена Ардуина
    - TEMP source - источник показаний температуры (процессор, видеокарта, максимум проц+видео, датчик 1, датчик 2)
   
   Что идёт в порт: 0-CPU temp, 1-GPU temp, 2-mother temp, 3-max HDD temp, 4-CPU load, 5-GPU load, 6-RAM use, 7-GPU memory use,
  */
// ------------------------ НАСТРОЙКИ ----------------------------
#define DRIVER_VERSION 1    // 0 - маркировка драйвера кончается на 4АТ, 1 - на 4Т
// ------------------------ НАСТРОЙКИ ----------------------------
 
// ----------------------- ПИНЫ ---------------------------
#define BTN1 A3             // первая кнопка
#define BTN2 A2             // вторая кнопка
// ----------------------- ПИНЫ ---------------------------
 
// -------------------- БИБЛИОТЕКИ ---------------------
#include <string.h>             // библиотека расширенной работы со строками
#include <Wire.h>               // библиотека для соединения
#include <LiquidCrystal_I2C.h>  // библтотека дислея
#include <TimerOne.h>           // библиотека таймера
// -------------------- БИБЛИОТЕКИ ---------------------
 
// -------- АВТОВЫБОР ОПРЕДЕЛЕНИЯ ДИСПЛЕЯ-------------
// Если кончается на 4Т - это 0х27. Если на 4АТ - 0х3f
#if (DRIVER_VERSION)
LiquidCrystal_I2C lcd(0x27, 16, 2);
#else
LiquidCrystal_I2C lcd(0x27, 16, 2);
#endif
// -------- АВТОВЫБОР ОПРЕДЕЛЕНИЯ ДИСПЛЕЯ-------------
 
#define printByte(args)  write(args);
#define TEMPERATURE_PRECISION 9
 // стартовый логотип
byte logo0[8] = {0b00011, 0b00110,  0b01110,  0b11111,  0b11011,  0b11001,  0b00000,  0b00000};
byte logo1[8] = {0b10000, 0b00001,  0b00001,  0b00001,  0b00000,  0b10001,  0b11011,  0b11111};
byte logo2[8] = {0b11100, 0b11000,  0b10001,  0b11011,  0b11111,  0b11100,  0b00000,  0b00000};
byte logo3[8] = {0b00000, 0b00001,  0b00011,  0b00111,  0b01101,  0b00111,  0b00010,  0b00000};
byte logo4[8] = {0b11111, 0b11111,  0b11011,  0b10001,  0b00000,  0b00000,  0b00000,  0b00000};
byte logo5[8] = {0b00000, 0b10000,  0b11000,  0b11100,  0b11110,  0b11100,  0b01000,  0b00000};
// значок градуса!!!! lcd.write(223);
byte degree[8] = {0b11100,  0b10100,  0b11100,  0b00000,  0b00000,  0b00000,  0b00000,  0b00000};
// правый край полосы загрузки
byte right_empty[8] = {0b11111,  0b00001,  0b00001,  0b00001,  0b00001,  0b00001,  0b00001,  0b11111};
// левый край полосы загрузки
byte left_empty[8] = {0b11111,  0b10000,  0b10000,  0b10000,  0b10000,  0b10000,  0b10000,  0b11111};
// центр полосы загрузки
byte center_empty[8] = {0b11111, 0b00000,  0b00000,  0b00000,  0b00000,  0b00000,  0b00000,  0b11111};
// блоки для построения графиков
byte row8[8] = {0b11111,  0b11111,  0b11111,  0b11111,  0b11111,  0b11111,  0b11111,  0b11111};
byte row7[8] = {0b00000,  0b11111,  0b11111,  0b11111,  0b11111,  0b11111,  0b11111,  0b11111};
byte row6[8] = {0b00000,  0b00000,  0b11111,  0b11111,  0b11111,  0b11111,  0b11111,  0b11111};
byte row5[8] = {0b00000,  0b00000,  0b00000,  0b11111,  0b11111,  0b11111,  0b11111,  0b11111};
byte row4[8] = {0b00000,  0b00000,  0b00000,  0b00000,  0b11111,  0b11111,  0b11111,  0b11111};
byte row3[8] = {0b00000,  0b00000,  0b00000,  0b00000,  0b00000,  0b11111,  0b11111,  0b11111};
byte row2[8] = {0b00000,  0b00000,  0b00000,  0b00000,  0b00000,  0b00000,  0b11111,  0b11111};
byte row1[8] = {0b00000,  0b00000,  0b00000,  0b00000,  0b00000,  0b00000,  0b00000,  0b11111};
 
char inData[82];       // массив входных значений (СИМВОЛЫ)
int PCdata[20];        // массив численных значений показаний с компьютера
byte PLOTmem[6][16];   // массив для хранения данных для построения графика (16 значений для 6 параметров)
byte blocks, halfs;
byte index = 0;
int display_mode = 6;
String string_convert;
unsigned long timeout, uptime_timer, plot_timer;
boolean lightState, reDraw_flag = 1, updateDisplay_flag, updateTemp_flag, timeOut_flag = 1;
int duty, LEDcolor;
int k, b, R, G, B, Rf, Gf, Bf;
byte mainTemp;
byte lines[] = {4, 5, 7, 6};
byte plotLines[] = {0, 1, 4, 5, 6, 7};    // 0-CPU temp, 1-GPU temp, 2-CPU load, 3-GPU load, 4-RAM load, 5-GPU memory
String perc;
unsigned long sec, mins, hrs;
byte temp1, temp2;
boolean btn1_sig, btn2_sig, btn1_flag, btn2_flag;
 
// Названия для легенды графиков
const char plot_0[] = "CPU";
const char plot_1[] = "GPU";
const char plot_2[] = "RAM";
 
const char plot_3[] = "temp";
const char plot_4[] = "load";
const char plot_5[] = "mem";
// названия ниже должны совпадать с массивами сверху и идти по порядку!
static const char *plotNames0[]  = {
  plot_0, plot_1, plot_0, plot_1, plot_2, plot_1
};
static const char *plotNames1[]  = {
  plot_3, plot_3, plot_4, plot_4, plot_4, plot_5
};
// 0-CPU temp, 1-GPU temp, 2-CPU load, 3-GPU load, 4-RAM load, 5-GPU memory
 
void setup() {
  Serial.begin(9600);
  pinMode(BTN1, INPUT_PULLUP);
  pinMode(BTN2, INPUT_PULLUP);
  // инициализация дисплея
  lcd.init();
  lcd.backlight();
  lcd.clear();            // очистить дисплей
  show_logo();            // показать логотип
  delay(2000);
  lcd.clear();            // очистить дисплей
  show_logo2();            // показать логотип
 
}
 
// ------------------------------ ОСНОВНОЙ ЦИКЛ -------------------------------
void loop() {
  parsing();                          // парсим строки с компьютера
  updatePlot();                       // обновляем массив данных графика
  dutyCalculate();                    // посчитать скважность для вентиляторов
  buttonsTick();                      // опрос кнопок и смена режимов
  updateDisplay();                    // обновить показания на дисплее
  timeoutTick();                      // проверка таймаута
}
// ------------------------------ ОСНОВНОЙ ЦИКЛ -------------------------------
 
void buttonsTick() {
  btn1_sig = !digitalRead(BTN1);
  btn2_sig = !digitalRead(BTN2);
  if (btn1_sig && !btn1_flag) {
    display_mode++;
    reDraw_flag = 1;
    if (display_mode > 9) display_mode = 0;
    btn1_flag = 1;
  }
  if (!btn1_sig && btn1_flag) {
    btn1_flag = 0;
  }
  if (btn2_sig && !btn2_flag) {
    display_mode--;
    reDraw_flag = 1;
    if (display_mode < 0) display_mode = 9;
    btn2_flag = 1;
  }
  if (!btn2_sig && btn2_flag) {
    btn2_flag = 0;
  }
}

void dutyCalculate() {
  if (PCdata[12] == 1)                  // если стоит галочка ManualFAN
    duty = PCdata[14];                  // скважность равна установленной ползунком
  else {                                // если нет
    switch (PCdata[18]) {
      case 0: mainTemp = PCdata[0];                   // взять опорную температуру как CPU
        break;
      case 1: mainTemp = PCdata[1];                   // взять опорную температуру как GPU
        break;
      case 2: mainTemp = max(PCdata[0], PCdata[1]);   // взять опорную температуру как максимум CPU и GPU
        break;
      case 3: mainTemp = temp1;
        break;
      case 4: mainTemp = temp2;
        break;
    }
    duty = map(mainTemp, PCdata[11], PCdata[10], PCdata[9], PCdata[8]);
    duty = constrain(duty, PCdata[9], PCdata[8]);
  }
}
void parsing() {
  while (Serial.available() > 0) {
    char aChar = Serial.read();
    if (aChar != 'E') {
      inData[index] = aChar;
      index++;
      inData[index] = '\0';
    } else {
      char *p = inData;
      char *str;
      index = 0;
      String value = "";
      while ((str = strtok_r(p, ";", &p)) != NULL) {
        string_convert = str;
        PCdata[index] = string_convert.toInt();
        index++;
      }
      index = 0;
      updateDisplay_flag = 1;
      updateTemp_flag = 1;
    }
    if (!timeOut_flag) {                                // если связь была потеряна, но восстановилась
    }
    timeout = millis();
    timeOut_flag = 1;
  }
}
void updatePlot() {
  if ((millis() - plot_timer) > (PCdata[17] * 1000)) {
    for (int i = 0; i < 6; i++) {           // для каждой строки параметров
      for (int j = 0; j < 15; j++) {        // каждый столбец параметров (кроме последнего)
        PLOTmem[i][j] = PLOTmem[i][j + 1];  // сдвинуть весь массив на шаг ВЛЕВО
      }
    }
    for (int i = 0; i < 6; i++) {
      // запомнить общее число полосок графика в ПОСЛЕДНИЙ элемент массива
      PLOTmem[i][15] = PCdata[plotLines[i]];
    }
    plot_timer = millis();
  }
}
void updateDisplay() {
  if (updateDisplay_flag) {
    if (reDraw_flag) {
      lcd.clear();
      switch (display_mode) {
        case 0:
        case 1:
        case 2:
        case 3:
        case 4:
        case 5: draw_plot_symb();
          break;
        case 6: draw_labels_11();
          break;
        case 7: draw_labels_12();
          break;
        case 8: draw_labels_21();
          break;
        case 9: draw_labels_22();
          break;
      }
      reDraw_flag = 0;
    }
    switch (display_mode) {
      case 0:
      case 1:
      case 2:
      case 3:
      case 4:
      case 5: draw_plot();
        break;
      case 6: draw_stats_11();
        break;
      case 7: draw_stats_12();
        break;
      case 8: draw_stats_21();
        break;
      case 9: draw_stats_22();
        break;
      case 50: debug();
        break;
    }
    updateDisplay_flag = 0;
  }
}
 
void draw_stats_11() {
  lcd.setCursor(4, 0); lcd.print(PCdata[0]); lcd.write(223);
  lcd.setCursor(13, 0); lcd.print(PCdata[4]);
  if (PCdata[4] < 10) perc = "% ";
  else if (PCdata[4] < 100) perc = "%";
  else perc = "";  lcd.print(perc);
  lcd.setCursor(4, 1); lcd.print(PCdata[1]); lcd.write(223);
  lcd.setCursor(13, 1); lcd.print(PCdata[5]);
  if (PCdata[5] < 10) perc = "% ";
  else if (PCdata[5] < 100) perc = "%";
  else perc = "";  lcd.print(perc);
 
  for (int i = 0; i < 2; i++) {
    byte line = ceil(PCdata[lines[i]] / 16);
    lcd.setCursor(7, i);
    if (line == 0) lcd.printByte(1)
      else lcd.printByte(4);
    for (int n = 1; n < 5; n++) {
      if (n < line) lcd.printByte(4);
      if (n >= line) lcd.printByte(2);
    }
    if (line == 6) lcd.printByte(4)
      else lcd.printByte(3);
  }
}
 
void draw_stats_12() {
  lcd.setCursor(13, 0); lcd.print(PCdata[7]);
  if (PCdata[7] < 10) perc = "% ";
  else if (PCdata[7] < 100) perc = "%";
  else perc = "";  lcd.print(perc);
  lcd.setCursor(13, 1); lcd.print(PCdata[6]);
  if (PCdata[6] < 10) perc = "% ";
  else if (PCdata[6] < 100) perc = "%";
  else perc = "";  lcd.print(perc);
 
  for (int i = 0; i < 2; i++) {
    byte line = ceil(PCdata[lines[i + 2]] / 16);
    lcd.setCursor(7, i);
    if (line == 0) lcd.printByte(1)
      else lcd.printByte(4);
    for (int n = 1; n < 5; n++) {
      if (n < line) lcd.printByte(4);
      if (n >= line) lcd.printByte(2);
    }
    if (line == 6) lcd.printByte(4)
      else lcd.printByte(3);
  }
}
 
void draw_stats_21() {
  lcd.setCursor(13, 0); lcd.print(duty);
  if ((duty) < 10) perc = "% ";
  else if ((duty) < 100) perc = "%";
  else perc = "";  lcd.print(perc);
 
  lcd.setCursor(3, 1); lcd.print(temp1); lcd.write(223);
  lcd.setCursor(11, 1); lcd.print(temp2); lcd.write(223);
 
  byte line = ceil(duty / 16);
  lcd.setCursor(6, 0);
  if (line == 0) lcd.printByte(1)
    else lcd.printByte(4);
  for (int n = 1; n < 5; n++) {
    if (n < line) lcd.printByte(4);
    if (n >= line) lcd.printByte(2);
  }
  if (line == 6) lcd.printByte(4)
    else lcd.printByte(3);
}
void draw_stats_22() {
  lcd.setCursor(2, 0); lcd.print(PCdata[2]); lcd.write(223);
  lcd.setCursor(10, 0); lcd.print(PCdata[3]); lcd.write(223);
 
  lcd.setCursor(7, 1);
  sec = (long)(millis() - uptime_timer) / 1000;
  hrs = floor((sec / 3600));
  mins = floor(sec - (hrs * 3600)) / 60;
  sec = sec - (hrs * 3600 + mins * 60);
  if (hrs < 10) lcd.print(0);
  lcd.print(hrs);
  lcd.print(":");
  if (mins < 10) lcd.print(0);
  lcd.print(mins);
  lcd.print(":");
  if (sec < 10) lcd.print(0);
  lcd.print(sec);
}
void draw_labels_11() {
  lcd.createChar(0, degree);
  lcd.createChar(1, left_empty);
  lcd.createChar(2, center_empty);
  lcd.createChar(3, right_empty);
  lcd.createChar(4, row8);
  lcd.setCursor(0, 0);
  lcd.print("CPU:");
  lcd.setCursor(0, 1);
  lcd.print("GPU:");
}
void draw_labels_12() {
  lcd.createChar(0, degree);
  lcd.createChar(1, left_empty);
  lcd.createChar(2, center_empty);
  lcd.createChar(3, right_empty);
  lcd.createChar(4, row8);
  lcd.setCursor(0, 0);
  lcd.print("GPUmem:");
  lcd.setCursor(0, 1);
  lcd.print("RAMuse:");
}
void draw_labels_21() {
  lcd.createChar(0, degree);
  lcd.createChar(1, left_empty);
  lcd.createChar(2, center_empty);
  lcd.createChar(3, right_empty);
  lcd.createChar(4, row8);
 
  lcd.setCursor(0, 0);
  lcd.print("FANsp:");
  lcd.setCursor(0, 1);
  lcd.print("T1: ");
  lcd.setCursor(8, 1);
  lcd.print("T2:");
}
void draw_labels_22() {
  lcd.createChar(0, degree);
  lcd.createChar(1, left_empty);
  lcd.createChar(2, center_empty);
  lcd.createChar(3, right_empty);
  lcd.createChar(4, row8);
 
  lcd.setCursor(0, 0);
  lcd.print("M:");
  lcd.setCursor(7, 0);
  lcd.print("HM:");
  lcd.setCursor(0, 1);
  lcd.print("UPTIME:");
}
void draw_legend() {
  byte data = PCdata[plotLines[display_mode]];
  lcd.setCursor(10, 0); lcd.print(data);
  if (display_mode > 1) {
    if (data < 10) perc = "% ";
    else if (data < 100) perc = "%";
    else {
      perc = "";
    }
    lcd.print(perc);
  } else {
    if (data < 10) {
      lcd.write(223);
      lcd.print("  ");
    } else if (data < 100) {
      lcd.write(223); lcd.print(" ");
    } else {
      lcd.write(223);
    }
  }
}
 
void draw_plot() {
  draw_legend();
 
  lcd.setCursor(0, 1);
  for (int i = 0; i < 16; i++) {
    lcd.printByte(constrain(map(PLOTmem[display_mode][i], 0, 100, 0, 7), 0, 7));
  }
}
 
void draw_plot_symb() {
  lcd.createChar(0, row1);
  lcd.createChar(1, row2);
  lcd.createChar(2, row3);
  lcd.createChar(3, row4);
  lcd.createChar(4, row5);
  lcd.createChar(5, row6);
  lcd.createChar(6, row7);
  lcd.createChar(7, row8);
  lcd.setCursor(0, 0);
  lcd.print(plotNames0[display_mode]);
  lcd.setCursor(3, 0);
  lcd.print(plotNames1[display_mode]);
  lcd.print(": ");
}
void timeoutTick() {
  if ((millis() - timeout > 5000) && timeOut_flag) {
    lcd.clear();
    lcd.setCursor(3, 0);
    lcd.print("CONNECTION");
    lcd.setCursor(5, 1);
    lcd.print("FAILED");
    timeOut_flag = 0;
    reDraw_flag = 1;
  }
}
void show_logo() {
  lcd.createChar(0, logo0);
  lcd.createChar(1, logo1);
  lcd.createChar(2, logo2);
  lcd.createChar(3, logo3);
  lcd.createChar(4, logo4);
  lcd.createChar(5, logo5);
  lcd.setCursor(0, 0);
  lcd.printByte(0);
  lcd.printByte(1);
  lcd.printByte(2);
  lcd.setCursor(0, 1);
  lcd.printByte(3);
  lcd.printByte(4);
  lcd.printByte(5);
  lcd.setCursor(4, 0);
  lcd.print("AlexGyver");
  lcd.setCursor(4, 1);
  lcd.print("Technologies");
}
void show_logo2() {
  lcd.setCursor(1, 0);
  lcd.print("modified by");
  lcd.setCursor(5, 1);
  lcd.print("klykov.net");
}
void debug() {
  lcd.clear();
  lcd.setCursor(0, 0);
  for (int j = 0; j < 5; j++) {
    lcd.print(PCdata[j]); lcd.print("  ");
  }
  lcd.setCursor(0, 1);
  for (int j = 6; j < 10; j++) {
    lcd.print(PCdata[j]); lcd.print("  ");
  }
  lcd.setCursor(0, 2);
  for (int j = 10; j < 15; j++) {
    lcd.print(PCdata[j]); lcd.print("  ");
  }
  lcd.setCursor(0, 3);
  for (int j = 15; j < 18; j++) {
    lcd.print(PCdata[j]); lcd.print("  ");
  }
}

 

ЗАРАНЕЕ СПАСИБО!!! <3

sadman41
Offline
Зарегистрирован: 19.10.2016

Какую проблему-то решить? Памяти допаять вам в МК что ли?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

wizaller пишет:

Помогите пожалуйста решить проблему. 

А проблема-то есть? Она работает или нет? Ну, мало памяти, но может хватает? Вы уж толком говорите.

А так - какие проблемы? Сокращать память, как я понимаю для Вас не вариант, ну тогда берите Мегу - туда влезет.

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

Фигня все это, у меня один скетч при 90 % стабильно уж пару месяцев работает, зависит от самого скетча, количества вызовов процедур (стек), количества сторонних библиотек и ещё кучи причин.

nik182
Offline
Зарегистрирован: 04.05.2015
Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Вряд ли будет работать, стоит переходить на иной камень. Мой ХШ подсказывает, что сама программа отожрет в худшем случае около 10 байт со стека. Но, в ней активно работает lcd.print(), который есть наследник как и Serial.print() от всего стека классов stream, а там жрется не так уж и мало, вот сколько точно уже не помню, но вряд ли меньше 14 байт. Там тучно есть около 9 вложенных вызовов "друг в друга", из-за чего и делал свою часть библиотеки - аналога работы с LCD, а это явно уже 18 байт стека как миниум, без локальных переменных.

ТС, даже интересно, работает стабильно или куда? :)

Genri5
Offline
Зарегистрирован: 31.05.2016

На Arduino 1.8.5 для Nano_V3  cкетч использует 10818 байт (35%) памяти устройства. Всего доступно30720 байт.Глобальные переменные используют 1000 байт (48%) динамической памяти, оставляя 1048 байт для локальных переменных. Максимум: 2048 байт.

Может это наведет Вас на правильную мысль. :-)

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

Arhat109-2 пишет:

 и Serial.print()

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

обычно удаляю все информационные сообщения когда скетч полностью оттестирован.

ven-til
Offline
Зарегистрирован: 13.02.2018

Вместо массивов row1-row8 можно генерировать програмно один массив на 8 байт с нужным символом в данный момент. Экономия в 56 байт обеспечена.