Проблема с выводом строки

Тема в разделе "Arduino & Shields", создана пользователем Sergej456, 22 сен 2017.

  1. Sergej456

    Sergej456 Нуб

    Всем привет!
    Появился очень интересный вопрос, на который не смог найти внятного ответа в интернете.
    Суть вот в чём:
    Есть код, в котором довольно много переменных String (работаю с M590, контроллер 328).
    По ходу работы в коде происходят проверки баланса, отправки смс и тд.
    Через некоторое время ( в зависимости от интенсивности работы) появляется интересное явление.
    Строки перестают писаться в порт. Что в Serial, что в SoftwareSerial. Пример:
    Код (C++):
    String x = "Kakoj-to tekst"; Serial.println(x);
    Будет выдавать просто новою строку, но без текста.
    Код (C++):
    Serial.println("Kakoj-to tekst");
    Точно так же. Причём в первом случае контроллер способен распознать, например,
    Код (C++):
    x.indexOf("tekst");
    А писать в порт не хочет. Встречаюсь с таким не первый раз, когда работаю с большим объёмом строк, который надо писать в порт.
     
  2. DIYMan

    DIYMan Guest

    Как вариант - не хватает памяти. Предположу, что много глобальных объектов типа String, посмотрите на досуге в исходниках, как там устроена работа с памятью ;)

    Выход: избавляться от глобальных объектов String по-максимуму.
     
    Sergej456 нравится это.
  3. DetSimen

    DetSimen Guest

    String - зло.
     
  4. Airbus

    Airbus Радиохулиган Модератор

    Просто тупо передавать в Serial "какойтотекст"
     
  5. Sergej456

    Sergej456 Нуб

    Моя идея была такая же. String используется у меня для того, чтобы писать AT команды и так-же в String принимается ответ. Чем можно попробовать заменить переменную типа String?
     
  6. DIYMan

    DIYMan Guest

    String юзать можно, надо только понимать про области видимости ;) Ещё раз: ГЛОБАЛЬНЫХ объектов типа String в идеале не должно быть, т.к. внутри там держится буфер до посинения. Локальных - сколь угодно. Приведу два примера, первый - неправильный, второй - правильный.

    Первый:

    Код (C++):
    String data;

    void loop()
    {
        data = "111";
        Serial.println(data);
    }
    Второй:
    Код (C++):

    void loop()
    {
        String data;
        data = "111";
        Serial.println(data);
    }
    В первом примере - буфер под 4 байта будет висеть до победного (плюс 6 байт, емнип, на экземпляр класса). Во втором - при заходе в loop будет выделена память, при выходе - память очистится. В первом случае имеем уменьшение свободной памяти (тем большее, чем длиннее строки вы скармливаете String - в случае скармливания более длинной строки тому же экземпляру - память перевыделяется, и так же висит занятой до посинения), во втором - всё норм. Как-то так.
     
    arkadyf, ostrov и Sergej456 нравится это.
  7. Sergej456

    Sergej456 Нуб

    Спасибо вам )
     
  8. Tomasina

    Tomasina Сушитель лампочек Модератор

    Еще совет - все текстовые неизменяемые фрагменты (обычно это всякие сообщения) пихать во флеш-память:

    Плохо:
    Код (C++):
    Serial.print("Temperatura: ");  Serial.println(temp);
    Хорошо:
    Код (C++):
    Serial.print(F("Temperatura: "));  Serial.println(temp);
    Реально экономия существенная.
     
    arkadyf, Sergej456 и DIYMan нравится это.
  9. Sergej456

    Sergej456 Нуб

    Совсем забыл про такой вариант. Вот он отлично помог, спасибо так же.
     
  10. Limoney

    Limoney Гик

    Еще хорошим тоном считается - ограничивать длину строки в памяти
    Код (C++):
    String inputString = "";
    inputString.reserve(100); // резервирует под строку 100 байт
     
     
    arkadyf и Sergej456 нравится это.
  11. DIYMan

    DIYMan Guest

    reserve - ничего не ограничивает, он просто ЗАРАНЕЕ резервирует запрошенную область памяти. Если сделать
    Код (C++):
    String s; s.reserve(2);
    и потом сделать
    Код (C++):
    s = "123";
    то память перевыделится, никакого ограничения в 2 - не будет. Это я к вашему "ограничивать длину строки в памяти", хотя в комментарии в коде вы поправились ("резервирует под строку 100 байт"). Короче, чтобы избежать двоякой интерпретации - я просто дополнил ;)
     
    arkadyf нравится это.
  12. b707

    b707 Гуру

    С макросом F() надо осторожнее. Если у вас в программе строка "Temperatura: " встречается больше 2х раз, от F("Temperatura: ") лучше отказаться. Дело в том, что в оперативной памяти одинаковые строки оптимизируются компилятором в один экземпляр, а перемещенные во флеш макросом F() - сохраняются каждый раз заново. Таким образом 1 строка "Temperatura: " займет в оперативке 14 байт и 10 таких строк потребуют для хранения ровно столько же. В случае же .макроса F() никакой оптимизации нет
    То есть, при первом использовании (F("Temperatura: ")); вы экономите 14 байи RAM, а при каждом следующем - только впустую расходуете флеш, причем довольно активно. Например, при 10 одинаковых строчках вы за экономию 14 байт RAM заплатите примерно 200-230 байтами флеша
     
    DIYMan и arkadyf нравится это.
  13. DIYMan

    DIYMan Guest

    Всё верно, спасибо за ценное дополнение, но по опыту чаще бьёшься за оперативу, чем за флеш - оперативы всегда мало.
     
  14. b707

    b707 Гуру

    Если согласны. тогда еще добавлю :) Есть более эффективное решение, чем F()
    Если повторящиеся строки загнать во флеш "вручную", квалификатором PROGMEM - на те же 10 вызовов строки "Temperatura" мы потратим всего порядка 30 байт флеша - вместо более чем 200х для F() (а экономия оперативки останется прежней - 14 байт)
    Код при этом будет такой:
    Код (C++):
    const char aa[] PROGMEM = "Temperatura: ";

    void setup() {
      // put your setup code here, to run once:
    Serial.println( (const __FlashStringHelper *) aa);
    Serial.println( (const __FlashStringHelper *) aa);
    Serial.println( (const __FlashStringHelper *) aa);
    Serial.println( (const __FlashStringHelper *) aa);
    Serial.println( (const __FlashStringHelper *) aa);
    Serial.println( (const __FlashStringHelper *) aa);
    Serial.println( (const __FlashStringHelper *) aa);
    Serial.println( (const __FlashStringHelper *) aa);
    Serial.println( (const __FlashStringHelper *) aa);
    Serial.println( (const __FlashStringHelper *) aa);
    }

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

    }
     
    DIYMan нравится это.
  15. DIYMan

    DIYMan Guest

    И это ценное замечание для начинающих, спасибо ;)

    З.Ы. Сам PROGMEM юзаю, правда, не всегда руки доходят до полной оптимизации (пересмотреть все F(), вычленить частые провторы и т.п.) - лень-матушка, пока флеша хватает, как-то не чешешься :)