Кто съедает динамическую память?

Smith2007
Offline
Зарегистрирован: 30.10.2017

Имеется простейший эмулятор AT команд в котором используются не более 20 переменных типа char.

/******************************************
 * Igor Kuznetcov
 *  ATCommand - эмулятор AT команд
 * 
 *
 * 
 * 
 *****************************************/

void ExecCMD(char cmd1, char cmd2, int val);
//int convert(char ascii[]);
void ReadCMD(void);

struct ATbuf_t {   // Структура команды
  char cmd1;     // команда 1
  char cmd2;     // команда 2
  char ascii_val[4]; // 4 байта данных ascii представление значения int
};

ATbuf_t ATbuf;  // Буфер команды

uint8_t i,j = 0;
uint8_t LF = 0x0a; 
uint8_t CR = 0x0d; 
boolean state = false; // При успешном приеме команды установить в true

// чтение команды с терминала Serial
// Предварительно инициализировать скорость терминала Serial.begin(115200)
//
void ReadCMD() {
  if (Serial.available() > 0) {
    char inChar = Serial.read();
    //Serial.println(inChar);
    switch (i) {
      case 0:
        if (inChar == 'A') {
          i++;
        } else {
          state = false;
        }
        break;
      case 1:
        if (inChar == 'T') {
          i++; 
        } else {
          i = 0;
          state = false;
        }
        break;
      case 2:  // cmd1
        if (inChar == CR) {
          ATbuf.cmd1 = 0x00;
          ATbuf.cmd2 = 0x00;
          for (j = 0; j > 3; j++) {ATbuf.ascii_val[j] = 0x0;};
          i = 0;
          state = true;
        } else {
          i++;
          ATbuf.cmd1 = inChar;
        }
        break;
      case 3:  // cmd2
        if (inChar == CR) {
          ATbuf.cmd2 = 0x00;
          for (j = 0; j > 3; j++) {ATbuf.ascii_val[j] = 0x0;};
          i = 0;
          state = true;
        } else {
          i++;
          ATbuf.cmd2 = inChar;
        }
        break;
      case 4:  
        if (inChar == CR) {
          for (j = 0; j > 3; j++) {ATbuf.ascii_val[j] = 0x0;};
          i = 0;
          state = true;
        } else {
          i++;
          ATbuf.ascii_val[0] = inChar;
        }
        break;
      case 5:
        if (inChar == CR) {
          i = 0;
          for (j=1; j > 3; j++) { ATbuf.ascii_val[j] = 0; };
          state = true;
        } else {
          i++;
          ATbuf.ascii_val[1] = inChar;
        }
        break;
      case 6:
        if (inChar == CR) {
          i = 0;
          for (j=2; j > 3; j++) { ATbuf.ascii_val[j] = 0; };
          state = true;
        } else {
          i++;
          ATbuf.ascii_val[2] = inChar;
        }
        break;
      case 7:
        if (inChar == CR) {
          i = 0;
          ATbuf.ascii_val[3] = 0;
          state = true;
        } else {
          i++;
          ATbuf.ascii_val[3] = inChar;
        }
        break;
      case 8:
        if (inChar == CR) {
          i = 0;
          state = true;
        }
        break;
    } // switch (i)
    if (state) {
      state = false;
      //int val = convert(ATbuf.ascii_val);
      //Serial.println( val );
      ExecCMD(ATbuf.cmd1, ATbuf.cmd2, atoi(ATbuf.ascii_val));
    }
  }
}

// парсинг AT команд
void ExecCMD(char cmd1, char cmd2, int val) {

  //Serial.println("ExecCMD");
  //Serial.print("cmd1, cmd2 = ");Serial.print(cmd1); Serial.print(cmd2); Serial.print(" val = ");Serial.println(val);
  if (cmd1 == 0x0 && cmd2 == 0x0) {
    Serial.println("OK");
  } else if (cmd1 == 'Z' && cmd2 == 0x0) {
    Serial.println("Reset to default: OK");
  } else if (cmd1 == 'Z' && cmd2 == 'C') {
    Serial.println("Reset AirComp to default: OK");
  } else if (cmd1 == 'Z' && cmd2 == 'F') {
    Serial.println("Reset BlockFront to default: OK");
  } else if (cmd1 == 'Z' && cmd2 == 'R') {
    Serial.println("Reset BlockRear to default: OK");
  } else if (cmd1 == 'Z' && cmd2 == 'V') {
    Serial.println("Reset Voltage to default: OK");
  } else if (cmd1 == 'W' && cmd2 == 0x0) {
    Serial.println("Write all: OK");
  } else if (cmd1 == 'W' && cmd2 == 'C') {
    Serial.println("Write AirComp: OK");
  } else if (cmd1 == 'W' && cmd2 == 'F') {
    Serial.println("Write BlockFront: OK");
  } else if (cmd1 == 'W' && cmd2 == 'R') {
    Serial.println("Write BlockRear: OK");
  } else if (cmd1 == 'W' && cmd2 == 'V') {
    Serial.println("Write Voltage: OK");
  } else if (cmd1 == 'E' && cmd2 == 'C') {
    if (val == 0 ) { 
      Serial.println("AirComp Disable: OK");
    } else {
      Serial.println("AirComp Enable: OK");
    }
  } else if (cmd1 == 'E' && cmd2 == 'F') {
    if (val == 0 ) { 
      Serial.println("BlockFront Disable: OK");
    } else {
      Serial.println("BlockFront Enable: OK");
    }
  } else if (cmd1 == 'E' && cmd2 == 'R') {
    if (val == 0 ) { 
      Serial.println("BlockRear Disable: OK");
    } else {
      Serial.println("BlockRear Enable: OK");
    }
  } else if (cmd1 == 'E' && cmd2 == 'V') {
    if (val == 0 ) { 
      Serial.println("Voltage Disable: OK");
    } else {
      Serial.println("Voltage Enable: OK");
    }
  } else if (cmd1 == 'D' && cmd2 == '0') {
      Serial.println("Debug Level0: OK");
  } else if (cmd1 == 'D' && cmd2 == '1') {
      Serial.println("Debug Level1: OK");
  } else if (cmd1 == 'D' && cmd2 == '2') {
      Serial.println("Debug Level2: OK");
  } else if (cmd1 == 'P' && cmd2 == '?') {
    Serial.print("P = "); Serial.println("8.0");
  } else if (cmd1 == 'P' && cmd2 == '0') {
    Serial.print("Pmin = "); Serial.println(val);
  } else if (cmd1 == 'P' && cmd2 == '1') {
    Serial.print("Pmax = "); Serial.println(val);
  } else if (cmd1 == 'P' && cmd2 == 'B') {
    Serial.print("Pblock = "); Serial.println(val);
  } else if (cmd1 == 'C' && cmd2 == 'T') {
    Serial.print("AirComp T = "); Serial.println(val);
  } else if (cmd1 == 'S' && cmd2 == '?') {
    Serial.print("Speed = "); Serial.println("10");
  } else if (cmd1 == 'F' && cmd2 == '?') {
     Serial.print("BlockFront = "); Serial.println("OFF");
  } else if (cmd1 == 'F' && cmd2 == '0') {
    Serial.print("BlockFront VmaxOn = "); Serial.println(val);
  } else if (cmd1 == 'F' && cmd2 == '2') {
    Serial.print("BlockFront VautoOff = "); Serial.println(val);
  } else if (cmd1 == 'R' && cmd2 == '?') {
    Serial.print("BlockRear = "); Serial.println("OFF");
  } else if (cmd1 == 'R' && cmd2 == '0') {
    Serial.print("BlockRear VmaxOn = "); Serial.println(val);
  } else if (cmd1 == 'R' && cmd2 == '2') {
    Serial.print("BlockRear VautoOff = "); Serial.println(val);
  } else if (cmd1 == 'V' && cmd2 == '?') {
    Serial.print("Voltage = "); Serial.println("12");
  } else if (cmd1 == 'V' && cmd2 == 'M') {
    Serial.print("Voltage min= "); Serial.println(val);
  } else if (cmd1 == 'I' && cmd2 == 'C') {
    Serial.print("AirComp Enable = "); Serial.println("1");
    Serial.print("AirComp State = "); Serial.println("RUN");
    Serial.print("AirComp Pressure = "); Serial.println("8.0");
    Serial.print("AirComp Pmax = "); Serial.println("8.5");
    Serial.print("AirComp Pmin = "); Serial.println("6.5");
    Serial.print("AirComp Pblock = "); Serial.println("6.0");
    Serial.print("AirComp Tmax = "); Serial.println("1200");
  } else if (cmd1 == 'I' && cmd2 == 'F') {
    Serial.print("BlockFront Enable = "); Serial.println("1");
    Serial.print("BlockFront State = "); Serial.println("OFF");
    Serial.print("BlockFront VmsxBlockOn = "); Serial.println("0");
    Serial.print("BlockFront VautoBlockOff = "); Serial.println("20");
  } else if (cmd1 == 'I' && cmd2 == 'R') {
    Serial.print("BlockRear Enable = "); Serial.println("1");
    Serial.print("BlockRear State = "); Serial.println("OFF");
    Serial.print("BlockRear VmsxBlockOn = "); Serial.println("0");
    Serial.print("BlockRear VautoBlockOff = "); Serial.println("20");
  } else if (cmd1 == 'I' && cmd2 == 'V') {
    Serial.print("Voltafe Enable = "); Serial.println("0");
    Serial.print("Voltafe Current = "); Serial.println("12.4");
    Serial.print("Voltafe Critical = "); Serial.println("10.0");
  } else if (cmd1 == 'I' && cmd2 == 'S') {
    Serial.print("Speed = "); Serial.println("0");
  }
}

/*
int convert(char ascii[]) {
  return atoi(ascii);
}
*/
void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);

}

void loop() {
  // put your main code here, to run repeatedly:
  ReadCMD();
}

При компиляции 

Скетч использует 6232 байт (21%) памяти устройства. Всего доступно 28672 байт.
Глобальные переменные используют 1190 байт (46%) динамической памяти, оставляя 1370 байт для локальных переменных. Максимум: 2560 байт.

Кто съедает память?

nik182
Offline
Зарегистрирован: 04.05.2015

Ох, сердце вещует, строчки в кавычках.

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

Думаю, что память жрут многочисленные строковые константы.

А вот условия циклов в строках 75, 86 и 96 - это сильно! Нипадецки вставляет :) Эта программа не для  LSD случаем? :)))))

b707
Offline
Зарегистрирован: 26.05.2017

Smith2007 пишет:

Кто съедает память?

Строк в кавычках - более 60-ти. Даже если они в среднем всего по 15 символов - уже порядка тысячи байт сожрали.

 

Smith2007
Offline
Зарегистрирован: 30.10.2017

b707 пишет:

Smith2007 пишет:

Кто съедает память?

Строк в кавычках - более 60-ти. Даже если они в среднем всего по 15 символов - уже порядка тысячи байт сожрали.

Как тогда сохранить строки, что бы они так не пожирали память?

Smith2007
Offline
Зарегистрирован: 30.10.2017

ЕвгенийП пишет:

Думаю, что память жрут многочисленные строковые константы.

А вот условия циклов в строках 75, 86 и 96 - это сильно! Нипадецки вставляет :) Эта программа не для  LSD случаем? :)))))

Нет, не для LSD

Таким способом думаю настройки делать в готовом приборе.

Знаю, что есть какая-то функция, что массив обнуляет, но на память не помню и оставил это на "потом".

Но как я понял - проблема в большом количестве строковых значений.

Можно их как-то иначе записать, что бы они не съедали столько памяти?

Smith2007
Offline
Зарегистрирован: 30.10.2017

А сколько динамической памяти считается нормальным для работы?

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Smith2007 пишет:

Можно их как-то иначе записать, что бы они не съедали столько памяти?

Ищите по ключевому слову PROGMEM.

Smith2007
Offline
Зарегистрирован: 30.10.2017

Спасибо за подсказку!

Вот что нашел. По моему это будет удобнее.

Макрос F()
Если в коде используется инструкция вроде...

Serial.print("Write something on the Serial Monitor");
...то выводимая на экран строка, как правило, сохраняется в RAM. Таким образом, если в вашем скетче много подобных строк, которые выводятся на Serial Monitor, то ресурс RAM-памяти может закончиться очень быстро. Однако если у вас есть свободное место во flash-памяти, то вышеуказанную инструкцию можно изменить таким образом, чтобы строка сохранялась именно туда. Для этого используется следующий синтаксис:

Serial.print(F("Write something on the Serial Monitor that is stored in FLASH"));

 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Удобнее чем что?

trembo
trembo аватар
Offline
Зарегистрирован: 08.04.2011

Удобнее чем PROGMEM, нет?

PROGMEM ? Неудобно : Удобно ;
F ? Удобно : Неудобно ;

Smith2007
Offline
Зарегистрирован: 30.10.2017

Ночью уже доделывал и выражение туманных мыслей обернулось в такой оборот :)

В общем оптимизировал со строками. Во первых я их укоротил существенно и вывожу в Serial.print с использованием макроса F()

С учетом прочих оптимизаций код программы уменьшился, а использование динамической памяти снизилсоь с 94% до 24% :)

 

Под словом "удобно" я имел в виду результат. Извиняюсь за мой французский :)

 

И спасибо всем откликнувшимся!

эээххх... учится и учиться еще

Smith2007
Offline
Зарегистрирован: 30.10.2017

ЕвгенийП пишет:

Думаю, что память жрут многочисленные строковые константы.

А вот условия циклов в строках 75, 86 и 96 - это сильно! Нипадецки вставляет :) Эта программа не для  LSD случаем? :)))))

Спасибо за подсказку. Это конечно ошибка из-за невнимательности

Правильно: for (j = 0; j < 4; j++) {ATbuf.ascii_val[j] = 0x0;};

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

Smith2007 пишет:

Знаю, что есть какая-то функция, что массив обнуляет

memset

Smith2007 пишет:

Можно их как-то иначе записать, что бы они не съедали столько памяти?

В дополнение к тому, что Вам уже посоветовали про PROGMEM (именно в дополнение, а не вместо), посмотрите сколько раз у Вас хранятся одинаковые подстроки, в разных фразах. Например,

BlockFront  - 11 раз
BlockRear - 11 раз
Write - 7 раз
Reset - 5 раз

Так храните их 1 раз. Это потребует 2-х Serial.print вместо одного (сначала вывести общую часть, а потом хвост), но память под строки сократит существенно.

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

Smith2007 пишет:

Правильно: for (j = 0; j < 4; j++) {ATbuf.ascii_val[j] = 0x0;};

Вот и отлично, что Вы это сами нашли!

Smith2007
Offline
Зарегистрирован: 30.10.2017

ЕвгенийП пишет:

Smith2007 пишет:

Знаю, что есть какая-то функция, что массив обнуляет

memset

Smith2007 пишет:

Можно их как-то иначе записать, что бы они не съедали столько памяти?

В дополнение к тому, что Вам уже посоветовали про PROGMEM (именно в дополнение, а не вместо), посмотрите сколько раз у Вас хранятся одинаковые подстроки, в разных фразах. Например,

BlockFront  - 11 раз
BlockRear - 11 раз
Write - 7 раз
Reset - 5 раз

Так храните их 1 раз. Это потребует 2-х Serial.print вместо одного (сначала вывести общую часть, а потом хвост), но память под строки сократит существенно.

Да, я это тоже увидел и уже исправил. В большинстве вообще убрал эти строки оставив только ОК, который тоже превратил 

const char OK[]="OK";

 

Smith2007
Offline
Зарегистрирован: 30.10.2017

А вот использовать memset в моем случае не везде удобно. В некоторых строках я обнуляю только часть массива. Пусть уже будет однотипно. Читается для меня легче. И думаю, что memset не сильно по ресурсам будет отличаться от for... для 4 элементов массива

 

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Если еще хочется прооптимизировать код то можно сделать так

126 void pgm_viev(char *name) {/*вывод из PROGMEM*/
127   char buffer[15];
128   strcpy_P(buffer, name);
129   Serial.print(buffer);
130 }

Смотрите здесь #257

Olej
Olej аватар
Offline
Зарегистрирован: 05.03.2018

Smith2007 пишет:

А вот использовать memset в моем случае не везде удобно. В некоторых строках я обнуляю только часть массива. Пусть уже будет однотипно. Читается для меня легче. И думаю, что memset не сильно по ресурсам будет отличаться от for... для 4 элементов массива

А вот memset() определённо лучше.

Даже в смысле читаемости кода:

Smith2007 пишет:

Правильно: for (j = 0; j < 4; j++) {ATbuf.ascii_val[j] = 0x0;};

memset( ATbuf.ascii_val, 0, 4 );

Даже когда это произвольные части массива.

 

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

Smith2007 пишет:

В некоторых строках я обнуляю только часть массива.

И что? memset-то чем виноват, сколько нужно, столько и обнуляйте.

Smith2007 пишет:

И думаю, что memset не сильно по ресурсам будет отличаться от for... для 4 элементов массива

Будет отличаться, скорее всего НЕ в пользу memeset'а

если хотите круто оптимизироваться, то для 4-х значений лучше написать безо всякого цикла:

a[0] =a[1] = a[2] = a[3] = 0;

И всего делов.

Можно ещё короче (суть та же)

*((long *)(ATbuf.ascii_val)) = 0;