Всем привет! Появился очень интересный вопрос, на который не смог найти внятного ответа в интернете. Суть вот в чём: Есть код, в котором довольно много переменных String (работаю с M590, контроллер 328). По ходу работы в коде происходят проверки баланса, отправки смс и тд. Через некоторое время ( в зависимости от интенсивности работы) появляется интересное явление. Строки перестают писаться в порт. Что в Serial, что в SoftwareSerial. Пример: Код (C++): String x = "Kakoj-to tekst"; Serial.println(x); Будет выдавать просто новою строку, но без текста. Код (C++): Serial.println("Kakoj-to tekst"); Точно так же. Причём в первом случае контроллер способен распознать, например, Код (C++): x.indexOf("tekst"); А писать в порт не хочет. Встречаюсь с таким не первый раз, когда работаю с большим объёмом строк, который надо писать в порт.
Как вариант - не хватает памяти. Предположу, что много глобальных объектов типа String, посмотрите на досуге в исходниках, как там устроена работа с памятью Выход: избавляться от глобальных объектов String по-максимуму.
Моя идея была такая же. String используется у меня для того, чтобы писать AT команды и так-же в String принимается ответ. Чем можно попробовать заменить переменную типа String?
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 - в случае скармливания более длинной строки тому же экземпляру - память перевыделяется, и так же висит занятой до посинения), во втором - всё норм. Как-то так.
Еще совет - все текстовые неизменяемые фрагменты (обычно это всякие сообщения) пихать во флеш-память: Плохо: Код (C++): Serial.print("Temperatura: "); Serial.println(temp); Хорошо: Код (C++): Serial.print(F("Temperatura: ")); Serial.println(temp); Реально экономия существенная.
Еще хорошим тоном считается - ограничивать длину строки в памяти Код (C++): String inputString = ""; inputString.reserve(100); // резервирует под строку 100 байт
reserve - ничего не ограничивает, он просто ЗАРАНЕЕ резервирует запрошенную область памяти. Если сделать Код (C++): String s; s.reserve(2); и потом сделать Код (C++): s = "123"; то память перевыделится, никакого ограничения в 2 - не будет. Это я к вашему "ограничивать длину строки в памяти", хотя в комментарии в коде вы поправились ("резервирует под строку 100 байт"). Короче, чтобы избежать двоякой интерпретации - я просто дополнил
С макросом F() надо осторожнее. Если у вас в программе строка "Temperatura: " встречается больше 2х раз, от F("Temperatura: ") лучше отказаться. Дело в том, что в оперативной памяти одинаковые строки оптимизируются компилятором в один экземпляр, а перемещенные во флеш макросом F() - сохраняются каждый раз заново. Таким образом 1 строка "Temperatura: " займет в оперативке 14 байт и 10 таких строк потребуют для хранения ровно столько же. В случае же .макроса F() никакой оптимизации нет То есть, при первом использовании (F("Temperatura: ")); вы экономите 14 байи RAM, а при каждом следующем - только впустую расходуете флеш, причем довольно активно. Например, при 10 одинаковых строчках вы за экономию 14 байт RAM заплатите примерно 200-230 байтами флеша
Всё верно, спасибо за ценное дополнение, но по опыту чаще бьёшься за оперативу, чем за флеш - оперативы всегда мало.
Если согласны. тогда еще добавлю Есть более эффективное решение, чем 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: }
И это ценное замечание для начинающих, спасибо З.Ы. Сам PROGMEM юзаю, правда, не всегда руки доходят до полной оптимизации (пересмотреть все F(), вычленить частые провторы и т.п.) - лень-матушка, пока флеша хватает, как-то не чешешься