Arduino

технологии

Неявные ошибки из-за нехватки памяти

Недавно при разработке контроллера столкнулся со странной ситуацией. При вызове функции String::replace() иногда возникала ошибка: результирующая строка содержала не тот текст, который я ожидал. В некоторых случаях конец строки "затирался" или состоял из символов явно не в той кодировке.

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

Но я проверил несколько раз все места в коде, где могли быть подобные ошибки, но код был абсолютно корректный.

В программе был один момент, который и подсказал мне разгадку: я реализовывал функционал небольшого web-сервера, а для этого неминуемо пришлось передавать и обрабатывать довольно длинны строки, содержащие код html-шаблонов и заголовков http запросов. Большие конечно только по меркам Arduino Uno, которая имеет всего 2кб оперативной памяти. Вот память и закончилась. А при переполнении памяти вы никак об этом не узнаете, кроме как по возникшим ошибкам.

Итак, признаки того, что у вас закончилась ООП:

  • ошибки "плавающие", возникают не всегда
  • в программе есть функционал обработки длинных строк или работы с большими числами
  • в качестве платформы вы используете Arduino Uno, Arduino Nano или другую плату с малым количеством ООП
  • при выводе значений переменных в сериальный порт в вывод попадают символы в кривой кодировке, подстроки из прошлых выводов и тд

Но это все косвенные признаки. Для того же, чтобы точно понять, что проблема именно в нехватке памяти, можно воспользоваться библиотекой MemoryFree.

А можно и не подключать библиотеку, а просто добавить в проект 2 файл: MemoryFree.h и MemoryFree.cpp, их код есть на https://playground.arduino.cc/Code/AvailableMemory/.

Далее просто в нужных местах кода, где вы предполагаете, что память закончилась, расставить:

#include <MemoryFree.h>
...
Serial.print("freeMemory()=");
Serial.println(freeMemory());

Близкое к 0 значение или же отрицательное говорит о проблеме с нехваткой памяти.

Вот пара советов, как этого избежать:

  • пополнить знания о работе с памятью в C++ (особенно если ваш опыт связан с разработкой на более высокоуровневых и/или скриптовых языках)
  • на первое время чаще в процессе разработки использовать функцию freeMemory(), пока экономия памяти не отложится в вашей памяти :)
  • если в программе есть функционал обработки больших строк или больших данных, то лучше выбрать плату с большим количеством памяти, как то Arduino Mega или даже Arduino Due
  • чаще очищать неиспользуемые переменные
  • длинные текстовые константы определять с использованием ключевого слова PROGMEM, тогда для их хранения будет использована flash-память, размер которой сильно превышает размер ООП

Пример использования PROGMEM:

#include <avr/pgmspace.h>
...
const char long_text[] PROGMEM = "Длинный текст, которые не меняется в процессе работы программы";
...

Arduino Mega с 8кБ ООП:

Arduino Due с 96кБ ООП: